author | Jan-Ivar Bruaroey <jib@mozilla.com> |
Sat, 28 Mar 2015 19:02:46 -0400 | |
changeset 236430 | 13068deb21935c908bc336764e8e3044972db61e |
parent 236429 | fcd0b0e6588a27bc2cc206a54fa9a393dc699a1b |
child 236431 | 826a680789047d34fe0294b521b656406601e4f5 |
push id | 57667 |
push user | rjesup@wgate.com |
push date | Sun, 29 Mar 2015 05:19:06 +0000 |
treeherder | mozilla-inbound@13068deb2193 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jesup |
bugs | 1046245 |
milestone | 39.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -36,16 +36,17 @@ #include "mozilla/PeerIdentity.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/File.h" #include "mozilla/dom/MediaStreamBinding.h" #include "mozilla/dom/MediaStreamTrackBinding.h" #include "mozilla/dom/GetUserMediaRequestBinding.h" #include "mozilla/Preferences.h" #include "mozilla/Base64.h" +#include "mozilla/ipc/BackgroundChild.h" #include "mozilla/media/MediaChild.h" #include "MediaTrackConstraints.h" #include "VideoUtils.h" #include "Latency.h" // For PR_snprintf #include "prprf.h" @@ -1509,17 +1510,17 @@ public: // pattern). We use .Then() to pass in a lambda to run back on this // thread once GetOriginKey resolves asynchronously . The "runnable" // nsRefPtr is "captured" (passed by value) into the lambda. nsRefPtr<media::ChildPledge<nsCString>> p = media::GetOriginKey(mOrigin, mInPrivateBrowsing); p->Then([runnable](nsCString result) mutable { runnable->mOriginKey = result; NS_DispatchToMainThread(runnable); - }, [](nsresult rv){}); + }); } // One of the Runnables have taken these. MOZ_ASSERT(!mOnSuccess && !mOnFailure); } private: MediaStreamConstraints mConstraints; nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess; @@ -2200,31 +2201,57 @@ MediaManager::Observe(nsISupports* aSubj nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); if (prefs) { prefs->RemoveObserver("media.navigator.video.default_width", this); prefs->RemoveObserver("media.navigator.video.default_height", this); prefs->RemoveObserver("media.navigator.video.default_fps", this); prefs->RemoveObserver("media.navigator.video.default_minfps", this); } - // Close off any remaining active windows. + // Because mMediaThread is not an nsThread, we must dispatch to it so it can + // clean up BackgroundChild. Continue stopping thread once this is done. + + class ShutdownTask : public Task { + public: + explicit ShutdownTask(nsRunnable* aReply) : mReply(aReply) {} + private: + virtual void + Run() + { + MOZ_ASSERT(MediaManager::IsInMediaThread()); + mozilla::ipc::BackgroundChild::CloseForCurrentThread(); + NS_DispatchToMainThread(mReply); + } + nsRefPtr<nsRunnable> mReply; + }; + + // Post ShutdownTask to execute on mMediaThread and pass in a lambda + // callback to be executed back on this thread once it is done. + // + // The lambda callback "captures" the 'this' pointer for member access. + // This is safe since this is guaranteed to be here since sSingleton isn't + // cleared until the lambda function clears it. + + MediaManager::GetMessageLoop()->PostTask(FROM_HERE, new ShutdownTask( + media::CallbackRunnable::New([this]() mutable { + // Close off any remaining active windows. MutexAutoLock lock(mMutex); GetActiveWindows()->Clear(); mActiveCallbacks.Clear(); mCallIds.Clear(); LOG(("Releasing MediaManager singleton and thread")); // Note: won't be released immediately as the Observer has a ref to us sSingleton = nullptr; if (mMediaThread) { mMediaThread->Stop(); } mBackend = nullptr; - } - + return NS_OK; + }))); return NS_OK; } else if (!strcmp(aTopic, "getUserMedia:response:allow")) { nsString key(aData); nsAutoPtr<GetUserMediaTask> task; mActiveCallbacks.RemoveAndForget(key, task); if (!task) { return NS_OK;
--- a/dom/media/systemservices/MediaChild.cpp +++ b/dom/media/systemservices/MediaChild.cpp @@ -20,32 +20,26 @@ extern PRLogModuleInfo *gMediaParentLog; #define LOG(args) #endif PRLogModuleInfo *gMediaChildLog; namespace mozilla { namespace media { -static PMediaChild* sChild = nullptr; - -template<typename ValueType> -ChildPledge<ValueType>::ChildPledge() -{ - MOZ_ASSERT(MediaManager::IsInMediaThread()); -} +static Child* sChild; template<typename ValueType> void ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor) { if (!sChild) { // Create PMedia by sending a message to the parent - sChild = aActor->SendPMediaConstructor(); + sChild = static_cast<Child*>(aActor->SendPMediaConstructor()); } - Run(static_cast<Child*>(sChild)); + Run(sChild); } template<typename ValueType> void ChildPledge<ValueType>::ActorFailed() { Pledge<ValueType>::Reject(NS_ERROR_UNEXPECTED); } @@ -60,21 +54,22 @@ GetOriginKey(const nsCString& aOrigin, b { class Pledge : public ChildPledge<nsCString> { public: explicit Pledge(const nsCString& aOrigin, bool aPrivateBrowsing) : mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {} private: ~Pledge() {} - void Run(PMediaChild* aMedia) + void Run(PMediaChild* aChild) { - aMedia->SendGetOriginKey(mOrigin, mPrivateBrowsing, &mValue); - LOG(("GetOriginKey for %s, result:", mOrigin.get(), mValue.get())); - Resolve(); + Child* child = static_cast<Child*>(aChild); + + uint32_t id = child->AddRequestPledge(*this); + child->SendGetOriginKey(id, mOrigin, mPrivateBrowsing); } const nsCString mOrigin; const bool mPrivateBrowsing; }; nsRefPtr<ChildPledge<nsCString>> p = new Pledge(aOrigin, aPrivateBrowsing); nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p); bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb); @@ -120,16 +115,61 @@ Child::Child() Child::~Child() { LOG(("~media::Child: %p", this)); sChild = nullptr; MOZ_COUNT_DTOR(Child); } +uint32_t Child::sRequestCounter = 0; + +uint32_t +Child::AddRequestPledge(ChildPledge<nsCString>& aPledge) +{ + uint32_t id = ++sRequestCounter; + nsRefPtr<ChildPledge<nsCString>> ptr(&aPledge); + mRequestPledges.AppendElement(PledgeEntry(id, ptr)); + return id; +} + +already_AddRefed<ChildPledge<nsCString>> +Child::RemoveRequestPledge(uint32_t aRequestId) +{ + for (PledgeEntry& entry : mRequestPledges) { + if (entry.first == aRequestId) { + nsRefPtr<ChildPledge<nsCString>> ref; + ref.swap(entry.second); + mRequestPledges.RemoveElement(entry); + return ref.forget(); + } + } + MOZ_ASSERT_UNREACHABLE("Received response with no matching media::ChildPledge!"); + return nullptr; +} + +bool +Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey) +{ + nsRefPtr<ChildPledge<nsCString>> pledge = RemoveRequestPledge(aRequestId); + if (pledge) { + pledge->Resolve(aKey); + } + return true; +} + PMediaChild* -CreateMediaChild() +AllocPMediaChild() { - return new Child(); + Child* obj = new Child(); + obj->AddRef(); + return obj; +} + +bool +DeallocPMediaChild(media::PMediaChild *aActor) +{ + static_cast<Child*>(aActor)->Release(); + return true; } } }
--- a/dom/media/systemservices/MediaChild.h +++ b/dom/media/systemservices/MediaChild.h @@ -20,40 +20,55 @@ namespace media { // functions, for the moment just: // // GetOriginKey() - get a cookie-like persisted unique key for a given origin. // SanitizeOriginKeys() - reset persisted unique keys. // GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges // (promise-like objects) with the future value. Use pledge.Then(func) to access. +class Child; + template<typename ValueType> class ChildPledge : public Pledge<ValueType>, public nsIIPCBackgroundChildCreateCallback { + friend Child; NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK NS_DECL_ISUPPORTS public: - explicit ChildPledge(); + explicit ChildPledge() {}; protected: virtual ~ChildPledge() {} virtual void Run(PMediaChild* aMedia) = 0; }; already_AddRefed<ChildPledge<nsCString>> GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing); already_AddRefed<ChildPledge<bool>> SanitizeOriginKeys(const uint64_t& aSinceWhen); class Child : public PMediaChild { + NS_INLINE_DECL_REFCOUNTING(Child) public: Child(); + + bool RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey); + + uint32_t AddRequestPledge(ChildPledge<nsCString>& aPledge); + already_AddRefed<ChildPledge<nsCString>> RemoveRequestPledge(uint32_t aRequestId); +private: virtual ~Child(); + + typedef std::pair<uint32_t,nsRefPtr<ChildPledge<nsCString>>> PledgeEntry; + static uint32_t sRequestCounter; + nsTArray<PledgeEntry> mRequestPledges; }; -PMediaChild* CreateMediaChild(); +PMediaChild* AllocPMediaChild(); +bool DeallocPMediaChild(PMediaChild *aActor); } // namespace media } // namespace mozilla #endif // mozilla_MediaChild_h
--- a/dom/media/systemservices/MediaParent.cpp +++ b/dom/media/systemservices/MediaParent.cpp @@ -2,16 +2,17 @@ /* vim: set sw=2 ts=8 et ft=cpp : */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MediaParent.h" #include "mozilla/Base64.h" +#include <mozilla/StaticMutex.h> #include "MediaUtils.h" #include "MediaEngine.h" #include "VideoUtils.h" #include "nsThreadUtils.h" #include "nsNetUtil.h" #include "nsILineInputStream.h" #include "nsAppDirectoryServiceDefs.h" @@ -30,16 +31,17 @@ PRLogModuleInfo *gMediaParentLog; // deviceIds to be unique per origin, to avoid them being supercookies. #define ORIGINKEYS_FILE "enumerate_devices.txt" #define ORIGINKEYS_VERSION "1" namespace mozilla { namespace media { +static StaticMutex gMutex; static ParentSingleton* sParentSingleton = nullptr; class ParentSingleton : public nsISupports { NS_DECL_ISUPPORTS class OriginKey { @@ -327,58 +329,102 @@ private: { sParentSingleton = nullptr; LOG((__FUNCTION__)); } public: static ParentSingleton* Get() { + // Protect creation of singleton and access from multiple Background threads. + // + // Multiple Background threads happen because sanitize.js calls us from the + // chrome process and gets a thread separate from the one servicing ipc from + // the content process. + + StaticMutexAutoLock lock(gMutex); if (!sParentSingleton) { sParentSingleton = new ParentSingleton(); } return sParentSingleton; } OriginKeysLoader mOriginKeys; OriginKeysTable mPrivateBrowsingOriginKeys; }; NS_IMPL_ISUPPORTS0(ParentSingleton) bool -Parent::RecvGetOriginKey(const nsCString& aOrigin, - const bool& aPrivateBrowsing, - nsCString* aKey) +Parent::RecvGetOriginKey(const uint32_t& aRequestId, + const nsCString& aOrigin, + const bool& aPrivateBrowsing) { - if (aPrivateBrowsing) { - mSingleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, *aKey); - } else { - mSingleton->mOriginKeys.GetOriginKey(aOrigin, *aKey); - } + // Hand over to stream-transport thread. + + nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + MOZ_ASSERT(sts); + nsRefPtr<ParentSingleton> singleton(mSingleton); + + nsRefPtr<PledgeRunnable<nsCString>> p = PledgeRunnable<nsCString>::New( + [singleton, aOrigin, aPrivateBrowsing](nsCString& aResult) { + if (aPrivateBrowsing) { + singleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, aResult); + } else { + singleton->mOriginKeys.GetOriginKey(aOrigin, aResult); + } + return NS_OK; + }); + nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } return true; + nsRefPtr<media::Parent> keepAlive(this); + p->Then([this, keepAlive, aRequestId](const nsCString& aKey) mutable { + if (!mDestroyed) { + unused << SendGetOriginKeyResponse(aRequestId, aKey); + } + return NS_OK; + }); } bool Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) { - mSingleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen); - mSingleton->mOriginKeys.Clear(aSinceWhen); + // Hand over to stream-transport thread. + + nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + MOZ_ASSERT(sts); + nsRefPtr<ParentSingleton> singleton(mSingleton); + + nsRefPtr<PledgeRunnable<bool>> p = PledgeRunnable<bool>::New( + [singleton, aSinceWhen](bool) { + singleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen); + singleton->mOriginKeys.Clear(aSinceWhen); + return NS_OK; + }); + nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } return true; } void Parent::ActorDestroy(ActorDestroyReason aWhy) { // No more IPC from here + mDestroyed = true; LOG((__FUNCTION__)); } Parent::Parent() : mSingleton(ParentSingleton::Get()) + , mDestroyed(false) { #if defined(PR_LOGGING) if (!gMediaParentLog) gMediaParentLog = PR_NewLogModule("MediaParent"); #endif LOG(("media::Parent: %p", this)); MOZ_COUNT_CTOR(Parent); @@ -386,15 +432,25 @@ Parent::Parent() Parent::~Parent() { LOG(("~media::Parent: %p", this)); MOZ_COUNT_DTOR(Parent); } -PMediaParent* CreateParent() +PMediaParent* +AllocPMediaParent() { - return new Parent(); + Parent* obj = new Parent(); + obj->AddRef(); + return obj; +} + +bool +DeallocPMediaParent(media::PMediaParent *aActor) +{ + static_cast<Parent*>(aActor)->Release(); + return true; } } }
--- a/dom/media/systemservices/MediaParent.h +++ b/dom/media/systemservices/MediaParent.h @@ -16,27 +16,31 @@ namespace mozilla { namespace media { // media::Parent implements the chrome-process side of ipc for media::Child APIs class ParentSingleton; class Parent : public PMediaParent { + NS_INLINE_DECL_REFCOUNTING(Parent) public: - virtual bool RecvGetOriginKey(const nsCString& aOrigin, - const bool& aPrivateBrowsing, - nsCString* aKey) override; + virtual bool RecvGetOriginKey(const uint32_t& aRequestId, + const nsCString& aOrigin, + const bool& aPrivateBrowsing) override; virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override; virtual void ActorDestroy(ActorDestroyReason aWhy) override; Parent(); +private: virtual ~Parent(); -private: + nsRefPtr<ParentSingleton> mSingleton; + bool mDestroyed; }; -PMediaParent* CreateParent(); +PMediaParent* AllocPMediaParent(); +bool DeallocPMediaParent(PMediaParent *aActor); } // namespace media } // namespace mozilla #endif // mozilla_MediaParent_h
--- a/dom/media/systemservices/MediaUtils.cpp +++ b/dom/media/systemservices/MediaUtils.cpp @@ -1,23 +1,13 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et ft=cpp : */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MediaUtils.h" -#include "mozilla/MediaManager.h" - namespace mozilla { namespace media { -template<typename ValueType> -Pledge<ValueType>::Pledge() - : mDone(false) - , mResult(NS_OK) -{ - MOZ_ASSERT(MediaManager::IsInMediaThread()); -} - } }
--- a/dom/media/systemservices/MediaUtils.h +++ b/dom/media/systemservices/MediaUtils.h @@ -3,16 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_MediaUtils_h #define mozilla_MediaUtils_h #include "nsAutoPtr.h" +#include "nsThreadUtils.h" namespace mozilla { namespace media { // A media::Pledge is a promise-like pattern for c++ that takes lambda functions. // // Asynchronous APIs that proxy to the chrome process and back, may return a // pledge to allow callers to use pledge.Then() to specify a lambda function to @@ -31,17 +32,23 @@ class Pledge public: FunctorsBase() {} virtual void Succeed(const ValueType& result) = 0; virtual void Fail(nsresult rv) = 0; virtual ~FunctorsBase() {}; }; public: - explicit Pledge(); + explicit Pledge() : mDone(false), mResult(NS_OK) {} + + template<typename OnSuccessType> + void Then(OnSuccessType aOnSuccess) + { + Then(aOnSuccess, [](nsresult){}); + } template<typename OnSuccessType, typename OnFailureType> void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure) { class F : public FunctorsBase { public: F(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure) @@ -66,16 +73,22 @@ public: mFunctors->Succeed(mValue); } else { mFunctors->Fail(mResult); } } } protected: + void Resolve(const ValueType& aValue) + { + mValue = aValue; + Resolve(); + } + void Resolve() { if (!mDone) { mDone = true; MOZ_ASSERT(mResult == NS_OK); if (mFunctors) { mFunctors->Succeed(mValue); } @@ -89,18 +102,96 @@ protected: mResult = rv; if (mFunctors) { mFunctors->Fail(mResult); } } } ValueType mValue; -private: +protected: bool mDone; nsresult mResult; +private: nsAutoPtr<FunctorsBase> mFunctors; }; +// General purpose runnable that also acts as a Pledge for the resulting value. +// Use PledgeRunnable<>::New() factory function to use with lambdas. + +template<typename ValueType> +class PledgeRunnable : public Pledge<ValueType>, public nsRunnable +{ +public: + template<typename OnRunType> + static PledgeRunnable<ValueType>* + New(OnRunType aOnRun) + { + class P : public PledgeRunnable<ValueType> + { + public: + explicit P(OnRunType& aOnRun) + : mOriginThread(NS_GetCurrentThread()) + , mOnRun(aOnRun) + , mHasRun(false) {} + private: + virtual ~P() {} + NS_IMETHODIMP + Run() + { + if (!mHasRun) { + P::mResult = mOnRun(P::mValue); + mHasRun = true; + return mOriginThread->Dispatch(this, NS_DISPATCH_NORMAL); + } + bool on; + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(mOriginThread->IsOnCurrentThread(&on))); + MOZ_RELEASE_ASSERT(on); + + if (NS_SUCCEEDED(P::mResult)) { + P::Resolve(); + } else { + P::Reject(P::mResult); + } + return NS_OK; + } + nsCOMPtr<nsIThread> mOriginThread; + OnRunType mOnRun; + bool mHasRun; + }; + + return new P(aOnRun); + } + +protected: + virtual ~PledgeRunnable() {} +}; + +// General purpose runnable with an eye toward lambdas + +namespace CallbackRunnable +{ +template<typename OnRunType> +class Impl : public nsRunnable +{ +public: + explicit Impl(OnRunType& aOnRun) : mOnRun(aOnRun) {} +private: + NS_IMETHODIMP + Run() + { + return mOnRun(); + } + OnRunType mOnRun; +}; + +template<typename OnRunType> +Impl<OnRunType>* +New(OnRunType aOnRun) +{ + return new Impl<OnRunType>(aOnRun); +} +} + } } #endif // mozilla_MediaUtils_h
--- a/dom/media/systemservices/PMedia.ipdl +++ b/dom/media/systemservices/PMedia.ipdl @@ -3,31 +3,33 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PContent; include protocol PBackground; namespace mozilla { namespace media { -sync protocol PMedia +protocol PMedia { manager PBackground; parent: /** - * Returns a persistent unique secret key for each origin. + * Requests a persistent unique secret key for each origin. * Has no expiry, but is cleared by age along with cookies. * This is needed by mediaDevices.enumerateDevices() to produce persistent * deviceIds that wont work cross-origin. */ - sync GetOriginKey(nsCString origin, bool privateBrowsing) returns (nsCString key); + GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing); /** - * On clear cookies. + * On clear cookies. Fire and forget. */ - sync SanitizeOriginKeys(uint64_t sinceWhen); + SanitizeOriginKeys(uint64_t aSinceWhen); - async __delete__(); +child: + GetOriginKeyResponse(uint32_t aRequestId, nsCString key); + __delete__(); }; } // namespace media } // namespace mozilla
--- a/ipc/glue/BackgroundChildImpl.cpp +++ b/ipc/glue/BackgroundChildImpl.cpp @@ -280,24 +280,23 @@ BackgroundChildImpl::DeallocPCacheStream { dom::cache::DeallocPCacheStreamControlChild(aActor); return true; } media::PMediaChild* BackgroundChildImpl::AllocPMediaChild() { - return media::CreateMediaChild(); + return media::AllocPMediaChild(); } bool BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor) { - delete aActor; - return true; + return media::DeallocPMediaChild(aActor); } } // namespace ipc } // namespace mozilla bool TestChild::Recv__delete__(const nsCString& aTestArg) {
--- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -359,24 +359,23 @@ BackgroundParentImpl::DeallocPBroadcastC delete static_cast<BroadcastChannelParent*>(aActor); return true; } media::PMediaParent* BackgroundParentImpl::AllocPMediaParent() { - return media::CreateParent(); + return media::AllocPMediaParent(); } bool BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor) { - delete aActor; - return true; + return media::DeallocPMediaParent(aActor); } namespace { class RegisterServiceWorkerCallback final : public nsRunnable { public: explicit RegisterServiceWorkerCallback(