Bug 1049552 Avoid PBackground addref off main thread in GetOrCreateForCurrentThread(). r=bent

This commit is contained in:
Ben Kelly 2014-08-20 19:42:00 -04:00
parent ceec6c67c2
commit 266c4cd30b
2 changed files with 123 additions and 58 deletions

View File

@ -1089,6 +1089,28 @@ public:
#endif
#ifdef ENABLE_TESTS
class TestPBackgroundCreateCallback MOZ_FINAL :
public nsIIPCBackgroundChildCreateCallback
{
public:
virtual void ActorCreated(PBackgroundChild* actor) MOZ_OVERRIDE
{
MOZ_RELEASE_ASSERT(actor);
}
virtual void ActorFailed() MOZ_OVERRIDE
{
MOZ_CRASH("TestPBackground() should not fail GetOrCreateForCurrentThread()");
}
private:
~TestPBackgroundCreateCallback()
{ }
public:
NS_DECL_ISUPPORTS;
};
void
TestPBackground()
{
@ -1123,6 +1145,11 @@ private:
{ }
};
#ifdef ENABLE_TESTS
NS_IMPL_ISUPPORTS(RuntimeService::WorkerThread::TestPBackgroundCreateCallback,
nsIIPCBackgroundChildCreateCallback);
#endif
BEGIN_WORKERS_NAMESPACE
// Entry point for main thread non-window globals.
@ -2698,12 +2725,12 @@ WorkerThreadPrimaryRunnable::Run()
return rv;
}
mThread->SetWorker(mWorkerPrivate);
#ifdef ENABLE_TESTS
mThread->TestPBackground();
#endif
mThread->SetWorker(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
{

View File

@ -312,7 +312,8 @@ class ChildImpl MOZ_FINAL : public BackgroundChildImpl
class ShutdownObserver;
class CreateActorRunnable;
class ParentCreateCallback;
class CreateCallbackRunnable;
class AlreadyCreatedCallbackRunnable;
class FailedCreateCallbackRunnable;
class OpenChildProcessActorRunnable;
class OpenMainProcessActorRunnable;
@ -463,6 +464,9 @@ private:
// Only called by IPDL.
virtual void
ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
static already_AddRefed<nsIIPCBackgroundChildCreateCallback>
GetNextCallback();
};
// -----------------------------------------------------------------------------
@ -690,21 +694,12 @@ private:
Failure() MOZ_OVERRIDE;
};
class ChildImpl::CreateCallbackRunnable : public nsRunnable
// Must be cancelable in order to dispatch on active worker threads
class ChildImpl::AlreadyCreatedCallbackRunnable MOZ_FINAL :
public nsCancelableRunnable
{
protected:
nsRefPtr<ChildImpl> mActor;
public:
CreateCallbackRunnable(already_AddRefed<ChildImpl>&& aActor)
: mActor(aActor)
{
// May be created on any thread!
MOZ_ASSERT(mActor);
}
CreateCallbackRunnable()
AlreadyCreatedCallbackRunnable()
{
// May be created on any thread!
}
@ -712,17 +707,33 @@ public:
NS_DECL_ISUPPORTS_INHERITED
protected:
virtual ~CreateCallbackRunnable();
virtual ~AlreadyCreatedCallbackRunnable()
{ }
static already_AddRefed<nsIIPCBackgroundChildCreateCallback>
GetNextCallback();
NS_DECL_NSIRUNNABLE
NS_DECL_NSICANCELABLERUNNABLE
};
class ChildImpl::FailedCreateCallbackRunnable MOZ_FINAL : public nsRunnable
{
public:
FailedCreateCallbackRunnable()
{
// May be created on any thread!
}
NS_DECL_ISUPPORTS_INHERITED
protected:
virtual ~FailedCreateCallbackRunnable()
{ }
NS_DECL_NSIRUNNABLE
};
class ChildImpl::OpenChildProcessActorRunnable MOZ_FINAL :
public ChildImpl::CreateCallbackRunnable
class ChildImpl::OpenChildProcessActorRunnable MOZ_FINAL : public nsRunnable
{
nsRefPtr<ChildImpl> mActor;
nsAutoPtr<Transport> mTransport;
ProcessHandle mProcessHandle;
@ -730,10 +741,11 @@ public:
OpenChildProcessActorRunnable(already_AddRefed<ChildImpl>&& aActor,
Transport* aTransport,
ProcessHandle aProcessHandle)
: CreateCallbackRunnable(Move(aActor)), mTransport(aTransport),
: mActor(aActor), mTransport(aTransport),
mProcessHandle(aProcessHandle)
{
AssertIsOnMainThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(aTransport);
}
@ -751,9 +763,9 @@ private:
NS_DECL_NSIRUNNABLE
};
class ChildImpl::OpenMainProcessActorRunnable MOZ_FINAL :
public ChildImpl::CreateCallbackRunnable
class ChildImpl::OpenMainProcessActorRunnable MOZ_FINAL : public nsRunnable
{
nsRefPtr<ChildImpl> mActor;
nsRefPtr<ParentImpl> mParentActor;
MessageLoop* mParentMessageLoop;
@ -761,7 +773,7 @@ public:
OpenMainProcessActorRunnable(already_AddRefed<ChildImpl>&& aChildActor,
already_AddRefed<ParentImpl> aParentActor,
MessageLoop* aParentMessageLoop)
: CreateCallbackRunnable(Move(aChildActor)), mParentActor(aParentActor),
: mActor(aChildActor), mParentActor(aParentActor),
mParentMessageLoop(aParentMessageLoop)
{
AssertIsOnMainThread();
@ -1648,9 +1660,9 @@ ChildImpl::GetOrCreateForCurrentThread(
}
if (threadLocalInfo->mActor) {
nsRefPtr<ChildImpl> actor = threadLocalInfo->mActor;
nsCOMPtr<nsIRunnable> runnable = new CreateCallbackRunnable(actor.forget());
// Runnable will use GetForCurrentThread() to retrieve actor again. This
// allows us to avoid addref'ing on the wrong thread.
nsCOMPtr<nsIRunnable> runnable = new AlreadyCreatedCallbackRunnable();
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
return true;
@ -1659,7 +1671,7 @@ ChildImpl::GetOrCreateForCurrentThread(
if (!created) {
// We have already started the sequence for opening the actor so there's
// nothing else we need to do here. This callback will be called after the
// first callback in CreateCallbackRunnable::Run().
// first callback in the schedule runnable.
return true;
}
@ -1729,17 +1741,9 @@ ChildImpl::GetThreadLocalForCurrentThread()
return threadLocalInfo->mConsumerThreadLocal;
}
ChildImpl::CreateCallbackRunnable::~CreateCallbackRunnable()
{
if (mActor) {
CRASH_IN_CHILD_PROCESS("Leaking actor!");
unused << mActor.forget();
}
}
// static
already_AddRefed<nsIIPCBackgroundChildCreateCallback>
ChildImpl::CreateCallbackRunnable::GetNextCallback()
ChildImpl::GetNextCallback()
{
// May run on any thread!
@ -1759,32 +1763,64 @@ ChildImpl::CreateCallbackRunnable::GetNextCallback()
return callback.forget();
}
NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::CreateCallbackRunnable, nsRunnable)
NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::AlreadyCreatedCallbackRunnable,
nsCancelableRunnable)
NS_IMETHODIMP
ChildImpl::CreateCallbackRunnable::Run()
ChildImpl::AlreadyCreatedCallbackRunnable::Run()
{
// May run on any thread!
nsRefPtr<ChildImpl> actor;
mActor.swap(actor);
// Report the current actor back in the callback.
PBackgroundChild* actor = ChildImpl::GetForCurrentThread();
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = GetNextCallback();
// If the current actor is null, do not create a new actor here. This likely
// means we are in the process of cleaning up a worker thread and do not want
// a new actor created. Unfortunately we cannot report back to the callback
// because the thread local is gone at this point. Instead simply do nothing
// and return.
if (NS_WARN_IF(!actor)) {
return NS_OK;
}
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
ChildImpl::GetNextCallback();
while (callback) {
if (actor) {
callback->ActorCreated(actor);
} else {
callback->ActorFailed();
}
callback->ActorCreated(actor);
callback = ChildImpl::GetNextCallback();
}
callback = GetNextCallback();
return NS_OK;
}
NS_IMETHODIMP
ChildImpl::AlreadyCreatedCallbackRunnable::Cancel()
{
// These are IPC infrastructure objects and need to run unconditionally.
Run();
return NS_OK;
}
NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::FailedCreateCallbackRunnable,
nsRunnable);
NS_IMETHODIMP
ChildImpl::FailedCreateCallbackRunnable::Run()
{
// May run on any thread!
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
ChildImpl::GetNextCallback();
while (callback) {
callback->ActorFailed();
callback = ChildImpl::GetNextCallback();
}
return NS_OK;
}
NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenChildProcessActorRunnable,
ChildImpl::CreateCallbackRunnable)
nsRunnable);
NS_IMETHODIMP
ChildImpl::OpenChildProcessActorRunnable::Run()
@ -1795,7 +1831,8 @@ ChildImpl::OpenChildProcessActorRunnable::Run()
MOZ_ASSERT(mActor);
MOZ_ASSERT(mTransport);
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = GetNextCallback();
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
ChildImpl::GetNextCallback();
MOZ_ASSERT(callback,
"There should be at least one callback when first creating the "
"actor!");
@ -1809,7 +1846,7 @@ ChildImpl::OpenChildProcessActorRunnable::Run()
while (callback) {
callback->ActorFailed();
callback = GetNextCallback();
callback = ChildImpl::GetNextCallback();
}
return NS_OK;
@ -1829,14 +1866,14 @@ ChildImpl::OpenChildProcessActorRunnable::Run()
while (callback) {
callback->ActorCreated(actor);
callback = GetNextCallback();
callback = ChildImpl::GetNextCallback();
}
return NS_OK;
}
NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenMainProcessActorRunnable,
ChildImpl::CreateCallbackRunnable)
nsRunnable);
NS_IMETHODIMP
ChildImpl::OpenMainProcessActorRunnable::Run()
@ -1848,7 +1885,8 @@ ChildImpl::OpenMainProcessActorRunnable::Run()
MOZ_ASSERT(mParentActor);
MOZ_ASSERT(mParentMessageLoop);
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = GetNextCallback();
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
ChildImpl::GetNextCallback();
MOZ_ASSERT(callback,
"There should be at least one callback when first creating the "
"actor!");
@ -1869,7 +1907,7 @@ ChildImpl::OpenMainProcessActorRunnable::Run()
while (callback) {
callback->ActorFailed();
callback = GetNextCallback();
callback = ChildImpl::GetNextCallback();
}
return NS_OK;
@ -1891,7 +1929,7 @@ ChildImpl::OpenMainProcessActorRunnable::Run()
while (callback) {
callback->ActorCreated(childActor);
callback = GetNextCallback();
callback = ChildImpl::GetNextCallback();
}
return NS_OK;
@ -2000,7 +2038,7 @@ ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget)
{
MOZ_ASSERT(aEventTarget);
nsCOMPtr<nsIRunnable> callbackRunnable = new CreateCallbackRunnable();
nsCOMPtr<nsIRunnable> callbackRunnable = new FailedCreateCallbackRunnable();
if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch CreateCallbackRunnable!");
}