Remove locks from MpdBuilder and lock at MpdNotifier level

- Since most use cases use notifier implementations that already uses
  locks, removing locks from MpdBuilder.
- Added locks to MpdNotifier::Flush() implementations. Multiple threads
  were able to write to the same file.

Issue #45, #49

Change-Id: I6e39824485672f40e6c947da97f1743fac174167
This commit is contained in:
Rintaro Kuroiwa 2015-11-12 13:40:52 -08:00
parent e251863130
commit 981041a3a8
8 changed files with 39 additions and 30 deletions

View File

@ -164,6 +164,7 @@ bool DashIopMpdNotifier::AddContentProtectionElement(
} }
bool DashIopMpdNotifier::Flush() { bool DashIopMpdNotifier::Flush() {
base::AutoLock auto_lock(lock_);
return WriteMpdToFile(output_path_, mpd_builder_.get()); return WriteMpdToFile(output_path_, mpd_builder_.get());
} }

View File

@ -14,6 +14,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/synchronization/lock.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/mpd_notifier_util.h" #include "packager/mpd/base/mpd_notifier_util.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"

View File

@ -760,6 +760,22 @@ TEST_P(DashIopMpdNotifierTest, UpdateEncryption) {
container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector)); container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector));
} }
// This test is mainly for tsan. Using both the notifier and the MpdBuilder.
// Although locks in MpdBuilder have been removed,
// https://github.com/google/edash-packager/issues/45
// This issue identified a bug where using SimpleMpdNotifier with multiple
// threads causes a deadlock. This tests with DashIopMpdNotifier.
TEST_F(DashIopMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) {
DashIopMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_,
empty_base_urls_, output_path_);
uint32_t container_id;
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo),
&container_id));
const uint32_t kAnySampleDuration = 1000;
EXPECT_TRUE(notifier.NotifySampleDuration(container_id, kAnySampleDuration));
EXPECT_TRUE(notifier.Flush());
}
INSTANTIATE_TEST_CASE_P(StaticAndDynamic, INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
DashIopMpdNotifierTest, DashIopMpdNotifierTest,
::testing::Values(MpdBuilder::kStatic, ::testing::Values(MpdBuilder::kStatic,

View File

@ -10,6 +10,7 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "packager/base/compiler_specific.h" #include "packager/base/compiler_specific.h"
#include "packager/base/synchronization/lock.h"
#include "packager/mpd/base/content_protection_element.h" #include "packager/mpd/base/content_protection_element.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"

View File

@ -401,16 +401,13 @@ MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options)
MpdBuilder::~MpdBuilder() {} MpdBuilder::~MpdBuilder() {}
void MpdBuilder::AddBaseUrl(const std::string& base_url) { void MpdBuilder::AddBaseUrl(const std::string& base_url) {
base::AutoLock scoped_lock(lock_);
base_urls_.push_back(base_url); base_urls_.push_back(base_url);
} }
AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) { AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) {
base::AutoLock scoped_lock(lock_);
scoped_ptr<AdaptationSet> adaptation_set( scoped_ptr<AdaptationSet> adaptation_set(
new AdaptationSet(adaptation_set_counter_.GetNext(), lang, mpd_options_, new AdaptationSet(adaptation_set_counter_.GetNext(), lang, mpd_options_,
type_, type_, &representation_counter_));
&representation_counter_));
DCHECK(adaptation_set); DCHECK(adaptation_set);
adaptation_sets_.push_back(adaptation_set.get()); adaptation_sets_.push_back(adaptation_set.get());
@ -418,13 +415,11 @@ AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) {
} }
bool MpdBuilder::WriteMpdToFile(media::File* output_file) { bool MpdBuilder::WriteMpdToFile(media::File* output_file) {
base::AutoLock scoped_lock(lock_);
DCHECK(output_file); DCHECK(output_file);
return WriteMpdToOutput(output_file); return WriteMpdToOutput(output_file);
} }
bool MpdBuilder::ToString(std::string* output) { bool MpdBuilder::ToString(std::string* output) {
base::AutoLock scoped_lock(lock_);
DCHECK(output); DCHECK(output);
return WriteMpdToOutput(output); return WriteMpdToOutput(output);
} }
@ -669,7 +664,6 @@ AdaptationSet::AdaptationSet(uint32_t adaptation_set_id,
AdaptationSet::~AdaptationSet() {} AdaptationSet::~AdaptationSet() {}
Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) { Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
base::AutoLock scoped_lock(lock_);
const uint32_t representation_id = representation_counter_->GetNext(); const uint32_t representation_id = representation_counter_->GetNext();
// Note that AdaptationSet outlive Representation, so this object // Note that AdaptationSet outlive Representation, so this object
// will die before AdaptationSet. // will die before AdaptationSet.
@ -715,14 +709,12 @@ Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
void AdaptationSet::AddContentProtectionElement( void AdaptationSet::AddContentProtectionElement(
const ContentProtectionElement& content_protection_element) { const ContentProtectionElement& content_protection_element) {
base::AutoLock scoped_lock(lock_);
content_protection_elements_.push_back(content_protection_element); content_protection_elements_.push_back(content_protection_element);
RemoveDuplicateAttributes(&content_protection_elements_.back()); RemoveDuplicateAttributes(&content_protection_elements_.back());
} }
void AdaptationSet::UpdateContentProtectionPssh(const std::string& drm_uuid, void AdaptationSet::UpdateContentProtectionPssh(const std::string& drm_uuid,
const std::string& pssh) { const std::string& pssh) {
base::AutoLock scoped_lock(lock_);
UpdateContentProtectionPsshHelper(drm_uuid, pssh, UpdateContentProtectionPsshHelper(drm_uuid, pssh,
&content_protection_elements_); &content_protection_elements_);
} }
@ -734,7 +726,6 @@ void AdaptationSet::AddRole(Role role) {
// Creates a copy of <AdaptationSet> xml element, iterate thru all the // Creates a copy of <AdaptationSet> xml element, iterate thru all the
// <Representation> (child) elements and add them to the copy. // <Representation> (child) elements and add them to the copy.
xml::ScopedXmlPtr<xmlNode>::type AdaptationSet::GetXml() { xml::ScopedXmlPtr<xmlNode>::type AdaptationSet::GetXml() {
base::AutoLock scoped_lock(lock_);
AdaptationSetXmlNode adaptation_set; AdaptationSetXmlNode adaptation_set;
if (!adaptation_set.AddContentProtectionElements( if (!adaptation_set.AddContentProtectionElements(
@ -827,8 +818,6 @@ int AdaptationSet::Group() const {
void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id, void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id,
uint64_t start_time, uint64_t start_time,
uint64_t duration) { uint64_t duration) {
base::AutoLock scoped_lock(lock_);
if (mpd_type_ == MpdBuilder::kDynamic) { if (mpd_type_ == MpdBuilder::kDynamic) {
CheckLiveSegmentAlignment(representation_id, start_time, duration); CheckLiveSegmentAlignment(representation_id, start_time, duration);
} else { } else {
@ -838,17 +827,15 @@ void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id,
} }
void AdaptationSet::OnSetFrameRateForRepresentation( void AdaptationSet::OnSetFrameRateForRepresentation(
uint32_t /* representation_id */, uint32_t representation_id,
uint32_t frame_duration, uint32_t frame_duration,
uint32_t timescale) { uint32_t timescale) {
base::AutoLock scoped_lock(lock_);
RecordFrameRate(frame_duration, timescale); RecordFrameRate(frame_duration, timescale);
} }
bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) { bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
DCHECK(timestamp_seconds); DCHECK(timestamp_seconds);
base::AutoLock scoped_lock(lock_);
double earliest_timestamp(-1); double earliest_timestamp(-1);
for (std::list<Representation*>::const_iterator iter = for (std::list<Representation*>::const_iterator iter =
representations_.begin(); representations_.begin();
@ -1064,14 +1051,12 @@ bool Representation::Init() {
void Representation::AddContentProtectionElement( void Representation::AddContentProtectionElement(
const ContentProtectionElement& content_protection_element) { const ContentProtectionElement& content_protection_element) {
base::AutoLock scoped_lock(lock_);
content_protection_elements_.push_back(content_protection_element); content_protection_elements_.push_back(content_protection_element);
RemoveDuplicateAttributes(&content_protection_elements_.back()); RemoveDuplicateAttributes(&content_protection_elements_.back());
} }
void Representation::UpdateContentProtectionPssh(const std::string& drm_uuid, void Representation::UpdateContentProtectionPssh(const std::string& drm_uuid,
const std::string& pssh) { const std::string& pssh) {
base::AutoLock scoped_lock(lock_);
UpdateContentProtectionPsshHelper(drm_uuid, pssh, UpdateContentProtectionPsshHelper(drm_uuid, pssh,
&content_protection_elements_); &content_protection_elements_);
} }
@ -1084,7 +1069,6 @@ void Representation::AddNewSegment(uint64_t start_time,
return; return;
} }
base::AutoLock scoped_lock(lock_);
if (state_change_listener_) if (state_change_listener_)
state_change_listener_->OnNewSegmentForRepresentation(start_time, duration); state_change_listener_->OnNewSegmentForRepresentation(start_time, duration);
if (IsContiguous(start_time, duration, size)) { if (IsContiguous(start_time, duration, size)) {
@ -1102,8 +1086,6 @@ void Representation::AddNewSegment(uint64_t start_time,
} }
void Representation::SetSampleDuration(uint32_t sample_duration) { void Representation::SetSampleDuration(uint32_t sample_duration) {
base::AutoLock scoped_lock(lock_);
if (media_info_.has_video_info()) { if (media_info_.has_video_info()) {
media_info_.mutable_video_info()->set_frame_duration(sample_duration); media_info_.mutable_video_info()->set_frame_duration(sample_duration);
if (state_change_listener_) { if (state_change_listener_) {
@ -1120,8 +1102,6 @@ void Representation::SetSampleDuration(uint32_t sample_duration) {
// AudioChannelConfig elements), AddContentProtectionElements*(), and // AudioChannelConfig elements), AddContentProtectionElements*(), and
// AddVODOnlyInfo() (Adds segment info). // AddVODOnlyInfo() (Adds segment info).
xml::ScopedXmlPtr<xmlNode>::type Representation::GetXml() { xml::ScopedXmlPtr<xmlNode>::type Representation::GetXml() {
base::AutoLock scoped_lock(lock_);
if (!HasRequiredMediaInfoFields()) { if (!HasRequiredMediaInfoFields()) {
LOG(ERROR) << "MediaInfo missing required fields."; LOG(ERROR) << "MediaInfo missing required fields.";
return xml::ScopedXmlPtr<xmlNode>::type(); return xml::ScopedXmlPtr<xmlNode>::type();
@ -1345,7 +1325,6 @@ std::string Representation::GetTextMimeType() const {
bool Representation::GetEarliestTimestamp(double* timestamp_seconds) { bool Representation::GetEarliestTimestamp(double* timestamp_seconds) {
DCHECK(timestamp_seconds); DCHECK(timestamp_seconds);
base::AutoLock scoped_lock(lock_);
if (segment_infos_.empty()) if (segment_infos_.empty())
return false; return false;

View File

@ -25,7 +25,6 @@
#include "packager/base/atomic_sequence_num.h" #include "packager/base/atomic_sequence_num.h"
#include "packager/base/gtest_prod_util.h" #include "packager/base/gtest_prod_util.h"
#include "packager/base/stl_util.h" #include "packager/base/stl_util.h"
#include "packager/base/synchronization/lock.h"
#include "packager/mpd/base/bandwidth_estimator.h" #include "packager/mpd/base/bandwidth_estimator.h"
#include "packager/mpd/base/content_protection_element.h" #include "packager/mpd/base/content_protection_element.h"
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
@ -144,7 +143,6 @@ class MpdBuilder {
std::list<std::string> base_urls_; std::list<std::string> base_urls_;
std::string availability_start_time_; std::string availability_start_time_;
base::Lock lock_;
base::AtomicSequenceNumber adaptation_set_counter_; base::AtomicSequenceNumber adaptation_set_counter_;
base::AtomicSequenceNumber representation_counter_; base::AtomicSequenceNumber representation_counter_;
@ -333,8 +331,6 @@ class AdaptationSet {
std::list<Representation*> representations_; std::list<Representation*> representations_;
::STLElementDeleter<std::list<Representation*> > representations_deleter_; ::STLElementDeleter<std::list<Representation*> > representations_deleter_;
base::Lock lock_;
base::AtomicSequenceNumber* const representation_counter_; base::AtomicSequenceNumber* const representation_counter_;
const uint32_t id_; const uint32_t id_;
@ -530,8 +526,6 @@ class Representation {
std::list<ContentProtectionElement> content_protection_elements_; std::list<ContentProtectionElement> content_protection_elements_;
std::list<SegmentInfo> segment_infos_; std::list<SegmentInfo> segment_infos_;
base::Lock lock_;
const uint32_t id_; const uint32_t id_;
std::string mime_type_; std::string mime_type_;
std::string codecs_; std::string codecs_;

View File

@ -129,6 +129,7 @@ bool SimpleMpdNotifier::AddContentProtectionElement(
} }
bool SimpleMpdNotifier::Flush() { bool SimpleMpdNotifier::Flush() {
base::AutoLock auto_lock(lock_);
return WriteMpdToFile(output_path_, mpd_builder_.get()); return WriteMpdToFile(output_path_, mpd_builder_.get());
} }

View File

@ -180,6 +180,22 @@ TEST_F(SimpleMpdNotifierTest, OnDemandNotifySampleDuration) {
notifier.NotifySampleDuration(kRepresentationId, kSampleDuration)); notifier.NotifySampleDuration(kRepresentationId, kSampleDuration));
} }
// This test is mainly for tsan. Using both the notifier and the MpdBuilder.
// Although locks in MpdBuilder have been removed,
// https://github.com/google/edash-packager/issues/45
// This issue identified a bug where using SimpleMpdNotifier with multiple
// threads causes a deadlock.
TEST_F(SimpleMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) {
SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_,
empty_base_urls_, output_path_);
uint32_t container_id;
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo),
&container_id));
const uint32_t kAnySampleDuration = 1000;
EXPECT_TRUE(notifier.NotifySampleDuration(container_id, kAnySampleDuration));
EXPECT_TRUE(notifier.Flush());
}
// Verify that NotifyNewSegment() for live works. // Verify that NotifyNewSegment() for live works.
TEST_F(SimpleMpdNotifierTest, LiveNotifyNewSegment) { TEST_F(SimpleMpdNotifierTest, LiveNotifyNewSegment) {
SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_, SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_,