Bug 1046245 - IO on STS thread + cleanup BackgroundChild on shutdown. r=jesup

This commit is contained in:
Jan-Ivar Bruaroey 2015-03-29 13:43:43 -04:00
parent c6676519f2
commit 924f8ae71f
10 changed files with 293 additions and 67 deletions

View File

@ -41,6 +41,7 @@
#include "mozilla/dom/GetUserMediaRequestBinding.h" #include "mozilla/dom/GetUserMediaRequestBinding.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/Base64.h" #include "mozilla/Base64.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/media/MediaChild.h" #include "mozilla/media/MediaChild.h"
#include "MediaTrackConstraints.h" #include "MediaTrackConstraints.h"
#include "VideoUtils.h" #include "VideoUtils.h"
@ -1497,9 +1498,12 @@ public:
result->AppendElement(source); result->AppendElement(source);
} }
} }
nsRefPtr<DeviceSuccessCallbackRunnable> runnable // In the case of failure with this newly allocated runnable, we
(new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure, // intentionally leak the runnable, because we've pawned mOnSuccess and
result.forget())); // mOnFailure onto it which are main thread objects unsafe to release here.
DeviceSuccessCallbackRunnable* runnable =
new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure,
result.forget());
if (mPrivileged) { if (mPrivileged) {
NS_DispatchToMainThread(runnable); NS_DispatchToMainThread(runnable);
} else { } else {
@ -1508,13 +1512,13 @@ public:
// GetOriginKey is an async API that returns a pledge (as promise-like // GetOriginKey is an async API that returns a pledge (as promise-like
// pattern). We use .Then() to pass in a lambda to run back on this // pattern). We use .Then() to pass in a lambda to run back on this
// thread once GetOriginKey resolves asynchronously . The "runnable" // thread once GetOriginKey resolves asynchronously . The "runnable"
// nsRefPtr is "captured" (passed by value) into the lambda. // pointer is "captured" (passed by value) into the lambda.
nsRefPtr<media::ChildPledge<nsCString>> p = nsRefPtr<media::ChildPledge<nsCString>> p =
media::GetOriginKey(mOrigin, mInPrivateBrowsing); media::GetOriginKey(mOrigin, mInPrivateBrowsing);
p->Then([runnable](nsCString result) mutable { p->Then([runnable](nsCString result) mutable {
runnable->mOriginKey = result; runnable->mOriginKey = result;
NS_DispatchToMainThread(runnable); NS_DispatchToMainThread(runnable);
}, [](nsresult rv){}); });
} }
// One of the Runnables have taken these. // One of the Runnables have taken these.
MOZ_ASSERT(!mOnSuccess && !mOnFailure); MOZ_ASSERT(!mOnSuccess && !mOnFailure);
@ -2205,8 +2209,34 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
prefs->RemoveObserver("media.navigator.video.default_minfps", 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); MutexAutoLock lock(mMutex);
GetActiveWindows()->Clear(); GetActiveWindows()->Clear();
mActiveCallbacks.Clear(); mActiveCallbacks.Clear();
@ -2218,8 +2248,8 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
mMediaThread->Stop(); mMediaThread->Stop();
} }
mBackend = nullptr; mBackend = nullptr;
} return NS_OK;
})));
return NS_OK; return NS_OK;
} else if (!strcmp(aTopic, "getUserMedia:response:allow")) { } else if (!strcmp(aTopic, "getUserMedia:response:allow")) {

View File

@ -24,22 +24,16 @@ PRLogModuleInfo *gMediaChildLog;
namespace mozilla { namespace mozilla {
namespace media { namespace media {
static PMediaChild* sChild = nullptr; static Child* sChild;
template<typename ValueType>
ChildPledge<ValueType>::ChildPledge()
{
MOZ_ASSERT(MediaManager::IsInMediaThread());
}
template<typename ValueType> void template<typename ValueType> void
ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor) ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor)
{ {
if (!sChild) { if (!sChild) {
// Create PMedia by sending a message to the parent // 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 template<typename ValueType> void
@ -64,11 +58,12 @@ GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing)
: mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {} : mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {}
private: private:
~Pledge() {} ~Pledge() {}
void Run(PMediaChild* aMedia) void Run(PMediaChild* aChild)
{ {
aMedia->SendGetOriginKey(mOrigin, mPrivateBrowsing, &mValue); Child* child = static_cast<Child*>(aChild);
LOG(("GetOriginKey for %s, result:", mOrigin.get(), mValue.get()));
Resolve(); uint32_t id = child->AddRequestPledge(*this);
child->SendGetOriginKey(id, mOrigin, mPrivateBrowsing);
} }
const nsCString mOrigin; const nsCString mOrigin;
const bool mPrivateBrowsing; const bool mPrivateBrowsing;
@ -124,10 +119,55 @@ Child::~Child()
MOZ_COUNT_DTOR(Child); MOZ_COUNT_DTOR(Child);
} }
PMediaChild* uint32_t Child::sRequestCounter = 0;
CreateMediaChild()
uint32_t
Child::AddRequestPledge(ChildPledge<nsCString>& aPledge)
{ {
return new Child(); 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*
AllocPMediaChild()
{
Child* obj = new Child();
obj->AddRef();
return obj;
}
bool
DeallocPMediaChild(media::PMediaChild *aActor)
{
static_cast<Child*>(aActor)->Release();
return true;
} }
} }

View File

@ -25,14 +25,17 @@ namespace media {
// GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges // GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
// (promise-like objects) with the future value. Use pledge.Then(func) to access. // (promise-like objects) with the future value. Use pledge.Then(func) to access.
class Child;
template<typename ValueType> template<typename ValueType>
class ChildPledge : public Pledge<ValueType>, class ChildPledge : public Pledge<ValueType>,
public nsIIPCBackgroundChildCreateCallback public nsIIPCBackgroundChildCreateCallback
{ {
friend Child;
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
public: public:
explicit ChildPledge(); explicit ChildPledge() {};
protected: protected:
virtual ~ChildPledge() {} virtual ~ChildPledge() {}
virtual void Run(PMediaChild* aMedia) = 0; virtual void Run(PMediaChild* aMedia) = 0;
@ -46,12 +49,24 @@ SanitizeOriginKeys(const uint64_t& aSinceWhen);
class Child : public PMediaChild class Child : public PMediaChild
{ {
NS_INLINE_DECL_REFCOUNTING(Child)
public: public:
Child(); 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(); 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 media
} // namespace mozilla } // namespace mozilla

View File

@ -7,6 +7,7 @@
#include "MediaParent.h" #include "MediaParent.h"
#include "mozilla/Base64.h" #include "mozilla/Base64.h"
#include <mozilla/StaticMutex.h>
#include "MediaUtils.h" #include "MediaUtils.h"
#include "MediaEngine.h" #include "MediaEngine.h"
@ -35,11 +36,12 @@ PRLogModuleInfo *gMediaParentLog;
namespace mozilla { namespace mozilla {
namespace media { namespace media {
static StaticMutex gMutex;
static ParentSingleton* sParentSingleton = nullptr; static ParentSingleton* sParentSingleton = nullptr;
class ParentSingleton : public nsISupports class ParentSingleton : public nsISupports
{ {
NS_DECL_ISUPPORTS NS_DECL_THREADSAFE_ISUPPORTS
class OriginKey class OriginKey
{ {
@ -332,6 +334,13 @@ private:
public: public:
static ParentSingleton* Get() 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) { if (!sParentSingleton) {
sParentSingleton = new ParentSingleton(); sParentSingleton = new ParentSingleton();
} }
@ -345,23 +354,58 @@ public:
NS_IMPL_ISUPPORTS0(ParentSingleton) NS_IMPL_ISUPPORTS0(ParentSingleton)
bool bool
Parent::RecvGetOriginKey(const nsCString& aOrigin, Parent::RecvGetOriginKey(const uint32_t& aRequestId,
const bool& aPrivateBrowsing, const nsCString& aOrigin,
nsCString* aKey) const bool& aPrivateBrowsing)
{ {
if (aPrivateBrowsing) { // Hand over to stream-transport thread.
mSingleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, *aKey);
} else { nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
mSingleton->mOriginKeys.GetOriginKey(aOrigin, *aKey); 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;
} }
nsRefPtr<media::Parent> keepAlive(this);
p->Then([this, keepAlive, aRequestId](const nsCString& aKey) mutable {
if (!mDestroyed) {
unused << SendGetOriginKeyResponse(aRequestId, aKey);
}
return NS_OK;
});
return true; return true;
} }
bool bool
Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen)
{ {
mSingleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen); // Hand over to stream-transport thread.
mSingleton->mOriginKeys.Clear(aSinceWhen);
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; return true;
} }
@ -369,11 +413,13 @@ void
Parent::ActorDestroy(ActorDestroyReason aWhy) Parent::ActorDestroy(ActorDestroyReason aWhy)
{ {
// No more IPC from here // No more IPC from here
mDestroyed = true;
LOG((__FUNCTION__)); LOG((__FUNCTION__));
} }
Parent::Parent() Parent::Parent()
: mSingleton(ParentSingleton::Get()) : mSingleton(ParentSingleton::Get())
, mDestroyed(false)
{ {
#if defined(PR_LOGGING) #if defined(PR_LOGGING)
if (!gMediaParentLog) if (!gMediaParentLog)
@ -391,9 +437,19 @@ Parent::~Parent()
MOZ_COUNT_DTOR(Parent); 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;
} }
} }

View File

@ -21,20 +21,24 @@ class ParentSingleton;
class Parent : public PMediaParent class Parent : public PMediaParent
{ {
NS_INLINE_DECL_REFCOUNTING(Parent)
public: public:
virtual bool RecvGetOriginKey(const nsCString& aOrigin, virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
const bool& aPrivateBrowsing, const nsCString& aOrigin,
nsCString* aKey) override; const bool& aPrivateBrowsing) override;
virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override; virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override; virtual void ActorDestroy(ActorDestroyReason aWhy) override;
Parent(); Parent();
virtual ~Parent();
private: private:
virtual ~Parent();
nsRefPtr<ParentSingleton> mSingleton; nsRefPtr<ParentSingleton> mSingleton;
bool mDestroyed;
}; };
PMediaParent* CreateParent(); PMediaParent* AllocPMediaParent();
bool DeallocPMediaParent(PMediaParent *aActor);
} // namespace media } // namespace media
} // namespace mozilla } // namespace mozilla

View File

@ -6,18 +6,8 @@
#include "MediaUtils.h" #include "MediaUtils.h"
#include "mozilla/MediaManager.h"
namespace mozilla { namespace mozilla {
namespace media { namespace media {
template<typename ValueType>
Pledge<ValueType>::Pledge()
: mDone(false)
, mResult(NS_OK)
{
MOZ_ASSERT(MediaManager::IsInMediaThread());
}
} }
} }

View File

@ -8,6 +8,7 @@
#define mozilla_MediaUtils_h #define mozilla_MediaUtils_h
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsThreadUtils.h"
namespace mozilla { namespace mozilla {
namespace media { namespace media {
@ -36,7 +37,13 @@ class Pledge
}; };
public: 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> template<typename OnSuccessType, typename OnFailureType>
void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure) void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
@ -71,6 +78,12 @@ public:
} }
protected: protected:
void Resolve(const ValueType& aValue)
{
mValue = aValue;
Resolve();
}
void Resolve() void Resolve()
{ {
if (!mDone) { if (!mDone) {
@ -94,12 +107,90 @@ protected:
} }
ValueType mValue; ValueType mValue;
private: protected:
bool mDone; bool mDone;
nsresult mResult; nsresult mResult;
private:
nsAutoPtr<FunctorsBase> mFunctors; 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);
}
}
} }
} }

View File

@ -8,25 +8,27 @@ include protocol PBackground;
namespace mozilla { namespace mozilla {
namespace media { namespace media {
sync protocol PMedia protocol PMedia
{ {
manager PBackground; manager PBackground;
parent: 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. * Has no expiry, but is cleared by age along with cookies.
* This is needed by mediaDevices.enumerateDevices() to produce persistent * This is needed by mediaDevices.enumerateDevices() to produce persistent
* deviceIds that wont work cross-origin. * 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 media

View File

@ -285,14 +285,13 @@ BackgroundChildImpl::DeallocPCacheStreamControlChild(PCacheStreamControlChild* a
media::PMediaChild* media::PMediaChild*
BackgroundChildImpl::AllocPMediaChild() BackgroundChildImpl::AllocPMediaChild()
{ {
return media::CreateMediaChild(); return media::AllocPMediaChild();
} }
bool bool
BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor) BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
{ {
delete aActor; return media::DeallocPMediaChild(aActor);
return true;
} }
} // namespace ipc } // namespace ipc

View File

@ -364,14 +364,13 @@ BackgroundParentImpl::DeallocPBroadcastChannelParent(
media::PMediaParent* media::PMediaParent*
BackgroundParentImpl::AllocPMediaParent() BackgroundParentImpl::AllocPMediaParent()
{ {
return media::CreateParent(); return media::AllocPMediaParent();
} }
bool bool
BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor) BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor)
{ {
delete aActor; return media::DeallocPMediaParent(aActor);
return true;
} }
namespace { namespace {