mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1013571 Reland support for PBackground on workers. r=bent
This commit is contained in:
parent
0280fddb89
commit
c765ce3a6e
@ -21,6 +21,7 @@
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "BackgroundChild.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "js/OldDebugAPI.h"
|
||||
#include "jsfriendapi.h"
|
||||
@ -40,6 +41,7 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollector.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsLayoutStatics.h"
|
||||
#include "nsNetUtil.h"
|
||||
@ -64,6 +66,12 @@
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
#include "BackgroundChildImpl.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "prrng.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -161,6 +169,10 @@ RuntimeService* gRuntimeService = nullptr;
|
||||
// Only non-null during the call to Init.
|
||||
RuntimeService* gRuntimeServiceDuringInit = nullptr;
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
bool gTestPBackground = false;
|
||||
#endif // ENABLE_TESTS
|
||||
|
||||
enum {
|
||||
ID_Worker = 0,
|
||||
ID_ChromeWorker,
|
||||
@ -899,6 +911,38 @@ private:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
};
|
||||
|
||||
class WorkerBackgroundChildCallback MOZ_FINAL :
|
||||
public nsIIPCBackgroundChildCreateCallback
|
||||
{
|
||||
bool* mDone;
|
||||
|
||||
public:
|
||||
WorkerBackgroundChildCallback(bool* aDone)
|
||||
: mDone(aDone)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mDone);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
private:
|
||||
~WorkerBackgroundChildCallback()
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE
|
||||
{
|
||||
*mDone = true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
ActorFailed() MOZ_OVERRIDE
|
||||
{
|
||||
*mDone = true;
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerThreadPrimaryRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
@ -941,6 +985,9 @@ private:
|
||||
~WorkerThreadPrimaryRunnable()
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
SynchronouslyCreatePBackground();
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
@ -1040,6 +1087,28 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
void
|
||||
TestPBackground()
|
||||
{
|
||||
using namespace mozilla::ipc;
|
||||
if (gTestPBackground) {
|
||||
// Randomize value to validate workers are not cross-posting messages.
|
||||
uint32_t testValue;
|
||||
PRSize randomSize = PR_GetRandomNoise(&testValue, sizeof(testValue));
|
||||
MOZ_RELEASE_ASSERT(randomSize == sizeof(testValue));
|
||||
nsCString testStr;
|
||||
testStr.AppendInt(testValue);
|
||||
testStr.AppendInt(reinterpret_cast<int64_t>(PR_GetCurrentThread()));
|
||||
PBackgroundChild* existingBackgroundChild =
|
||||
BackgroundChild::GetForCurrentThread();
|
||||
MOZ_RELEASE_ASSERT(existingBackgroundChild);
|
||||
bool ok = existingBackgroundChild->SendPBackgroundTestConstructor(testStr);
|
||||
MOZ_RELEASE_ASSERT(ok);
|
||||
}
|
||||
}
|
||||
#endif // #ENABLE_TESTS
|
||||
|
||||
private:
|
||||
WorkerThread()
|
||||
: nsThread(nsThread::NOT_MAIN_THREAD, WORKER_STACK_SIZE),
|
||||
@ -1245,6 +1314,10 @@ RuntimeService::GetOrCreateService()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
gTestPBackground = mozilla::Preferences::GetBool("pbackground.testing", false);
|
||||
#endif // ENABLE_TESTS
|
||||
|
||||
// The observer service now owns us until shutdown.
|
||||
gRuntimeService = service;
|
||||
}
|
||||
@ -1534,10 +1607,6 @@ RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
thread->SetAcceptingNonWorkerRunnables(false);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2526,8 +2595,20 @@ RuntimeService::WorkerThread::Observer::OnProcessNextEvent(
|
||||
bool aMayWait,
|
||||
uint32_t aRecursionDepth)
|
||||
{
|
||||
using mozilla::ipc::BackgroundChild;
|
||||
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!aMayWait);
|
||||
|
||||
// If the PBackground child is not created yet, then we must permit
|
||||
// blocking event processing to support SynchronouslyCreatePBackground().
|
||||
// If this occurs then we are spinning on the event queue at the start of
|
||||
// PrimaryWorkerRunnable::Run() and don't want to process the event in
|
||||
// mWorkerPrivate yet.
|
||||
if (aMayWait) {
|
||||
MOZ_ASSERT(aRecursionDepth == 2);
|
||||
MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mWorkerPrivate->OnProcessNextEvent(aRecursionDepth);
|
||||
return NS_OK;
|
||||
@ -2571,11 +2652,15 @@ LogViolationDetailsRunnable::Run()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(WorkerBackgroundChildCallback, nsIIPCBackgroundChildCreateCallback)
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable, nsRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThreadPrimaryRunnable::Run()
|
||||
{
|
||||
using mozilla::ipc::BackgroundChild;
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
@ -2594,6 +2679,19 @@ WorkerThreadPrimaryRunnable::Run()
|
||||
|
||||
profiler_register_thread(threadName.get(), &stackBaseGuess);
|
||||
|
||||
// Note: SynchronouslyCreatePBackground() must be called prior to
|
||||
// mThread->SetWorker() in order to avoid accidentally consuming
|
||||
// worker messages here.
|
||||
nsresult rv = SynchronouslyCreatePBackground();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// XXX need to fire an error at parent.
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
mThread->TestPBackground();
|
||||
#endif
|
||||
|
||||
mThread->SetWorker(mWorkerPrivate);
|
||||
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
@ -2627,6 +2725,12 @@ WorkerThreadPrimaryRunnable::Run()
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
mThread->TestPBackground();
|
||||
#endif
|
||||
|
||||
BackgroundChild::CloseForCurrentThread();
|
||||
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (stack) {
|
||||
stack->sampleRuntime(nullptr);
|
||||
@ -2665,6 +2769,38 @@ WorkerThreadPrimaryRunnable::Run()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerThreadPrimaryRunnable::SynchronouslyCreatePBackground()
|
||||
{
|
||||
using mozilla::ipc::BackgroundChild;
|
||||
|
||||
MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
|
||||
|
||||
bool done = false;
|
||||
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
|
||||
new WorkerBackgroundChildCallback(&done);
|
||||
|
||||
if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
while (!done) {
|
||||
if (NS_WARN_IF(!NS_ProcessNextEvent(mThread, true /* aMayWait */))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!BackgroundChild::GetForCurrentThread())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mThread->SetAcceptingNonWorkerRunnables(false);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable::FinishedRunnable,
|
||||
nsRunnable)
|
||||
|
||||
|
@ -37,6 +37,11 @@ class PBackgroundChild;
|
||||
// (assuming success) GetForCurrentThread() will return the same actor every
|
||||
// time.
|
||||
//
|
||||
// CloseForCurrentThread() will close the current PBackground actor. Subsequent
|
||||
// calls to GetForCurrentThread will return null. CloseForCurrentThread() may
|
||||
// only be called exactly once for each thread-specific actor. Currently it is
|
||||
// illegal to call this before the PBackground actor has been created.
|
||||
//
|
||||
// The PBackgroundChild actor and all its sub-protocol actors will be
|
||||
// automatically destroyed when its designated thread completes.
|
||||
class BackgroundChild MOZ_FINAL
|
||||
@ -56,6 +61,10 @@ public:
|
||||
static bool
|
||||
GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
|
||||
|
||||
// See above.
|
||||
static void
|
||||
CloseForCurrentThread();
|
||||
|
||||
private:
|
||||
// Only called by ContentChild or ContentParent.
|
||||
static void
|
||||
|
@ -350,6 +350,8 @@ class ChildImpl MOZ_FINAL : public BackgroundChildImpl
|
||||
nsIThread* mBoundThread;
|
||||
#endif
|
||||
|
||||
DebugOnly<bool> mActorDestroyed;
|
||||
|
||||
public:
|
||||
static bool
|
||||
OpenProtocolOnMainThread(nsIEventTarget* aEventTarget);
|
||||
@ -372,8 +374,15 @@ public:
|
||||
THREADSAFETY_ASSERT(current);
|
||||
}
|
||||
|
||||
void
|
||||
AssertActorDestroyed()
|
||||
{
|
||||
MOZ_ASSERT(mActorDestroyed, "ChildImpl::ActorDestroy not called in time");
|
||||
}
|
||||
|
||||
ChildImpl()
|
||||
: mBoundThread(nullptr)
|
||||
, mActorDestroyed(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
@ -397,6 +406,10 @@ private:
|
||||
static bool
|
||||
GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
|
||||
|
||||
// Forwarded from BackgroundChild.
|
||||
static void
|
||||
CloseForCurrentThread();
|
||||
|
||||
// Forwarded from BackgroundChildImpl.
|
||||
static BackgroundChildImpl::ThreadLocal*
|
||||
GetThreadLocalForCurrentThread();
|
||||
@ -409,6 +422,18 @@ private:
|
||||
if (threadLocalInfo) {
|
||||
if (threadLocalInfo->mActor) {
|
||||
threadLocalInfo->mActor->Close();
|
||||
threadLocalInfo->mActor->AssertActorDestroyed();
|
||||
|
||||
// Since the actor is created on the main thread it must only
|
||||
// be released on the main thread as well.
|
||||
if (!NS_IsMainThread()) {
|
||||
ChildImpl* actor;
|
||||
threadLocalInfo->mActor.forget(&actor);
|
||||
|
||||
nsCOMPtr<nsIRunnable> releaser =
|
||||
NS_NewNonOwningRunnableMethod(actor, &ChildImpl::Release);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(releaser)));
|
||||
}
|
||||
}
|
||||
delete threadLocalInfo;
|
||||
}
|
||||
@ -419,7 +444,9 @@ private:
|
||||
|
||||
// This class is reference counted.
|
||||
~ChildImpl()
|
||||
{ }
|
||||
{
|
||||
AssertActorDestroyed();
|
||||
}
|
||||
|
||||
void
|
||||
SetBoundThread()
|
||||
@ -835,6 +862,13 @@ BackgroundChild::GetOrCreateForCurrentThread(
|
||||
return ChildImpl::GetOrCreateForCurrentThread(aCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
BackgroundChild::CloseForCurrentThread()
|
||||
{
|
||||
ChildImpl::CloseForCurrentThread();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// BackgroundChildImpl Public Methods
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -1578,7 +1612,11 @@ ChildImpl::GetForCurrentThread()
|
||||
auto threadLocalInfo =
|
||||
static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
|
||||
|
||||
return threadLocalInfo ? threadLocalInfo->mActor : nullptr;
|
||||
if (!threadLocalInfo) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return threadLocalInfo->mActor;
|
||||
}
|
||||
|
||||
// static
|
||||
@ -1642,6 +1680,33 @@ ChildImpl::GetOrCreateForCurrentThread(
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
ChildImpl::CloseForCurrentThread()
|
||||
{
|
||||
MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
|
||||
"BackgroundChild::Startup() was never called!");
|
||||
auto threadLocalInfo =
|
||||
static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
|
||||
|
||||
// If we don't have a thread local we are in one of these conditions:
|
||||
// 1) Startup has not completed and we are racing
|
||||
// 2) We were called again after a previous close or shutdown
|
||||
// For now, these should not happen, so crash. We can add extra complexity
|
||||
// in the future if it turns out we need to support these cases.
|
||||
if (!threadLocalInfo) {
|
||||
MOZ_CRASH("Attempting to close a non-existent PBackground actor!");
|
||||
}
|
||||
|
||||
if (threadLocalInfo->mActor) {
|
||||
threadLocalInfo->mActor->FlushPendingInterruptQueue();
|
||||
}
|
||||
|
||||
// Clearing the thread local will synchronously close the actor.
|
||||
DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
|
||||
MOZ_ASSERT(status == PR_SUCCESS);
|
||||
}
|
||||
|
||||
// static
|
||||
BackgroundChildImpl::ThreadLocal*
|
||||
ChildImpl::GetThreadLocalForCurrentThread()
|
||||
@ -1946,6 +2011,9 @@ ChildImpl::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
AssertIsOnBoundThread();
|
||||
|
||||
MOZ_ASSERT(!mActorDestroyed);
|
||||
mActorDestroyed = true;
|
||||
|
||||
BackgroundChildImpl::ActorDestroy(aWhy);
|
||||
}
|
||||
|
||||
|
@ -1662,10 +1662,11 @@ MessageChannel::Close()
|
||||
}
|
||||
|
||||
if (ChannelOpening == mChannelState) {
|
||||
// Mimic CloseWithError().
|
||||
// SynchronouslyClose() waits for an ack from the other side, so
|
||||
// the opening sequence should complete before this returns.
|
||||
SynchronouslyClose();
|
||||
mChannelState = ChannelError;
|
||||
PostErrorNotifyTask();
|
||||
NotifyMaybeChannelError();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsICancelableRunnable.h"
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/logging.h"
|
||||
@ -40,7 +41,7 @@ static mozilla::DebugOnly<MessagePump::Delegate*> gFirstDelegate;
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class DoWorkRunnable MOZ_FINAL : public nsIRunnable,
|
||||
class DoWorkRunnable MOZ_FINAL : public nsICancelableRunnable,
|
||||
public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
@ -53,12 +54,15 @@ public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSICANCELABLERUNNABLE
|
||||
|
||||
private:
|
||||
~DoWorkRunnable()
|
||||
{ }
|
||||
|
||||
MessagePump* mPump;
|
||||
// DoWorkRunnable is designed as a stateless singleton. Do not add stateful
|
||||
// members here!
|
||||
};
|
||||
|
||||
} /* namespace ipc */
|
||||
@ -211,7 +215,8 @@ MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate)
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(DoWorkRunnable, nsIRunnable, nsITimerCallback)
|
||||
NS_IMPL_ISUPPORTS(DoWorkRunnable, nsIRunnable, nsITimerCallback,
|
||||
nsICancelableRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
DoWorkRunnable::Run()
|
||||
@ -242,6 +247,20 @@ DoWorkRunnable::Notify(nsITimer* aTimer)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DoWorkRunnable::Cancel()
|
||||
{
|
||||
// Workers require cancelable runnables, but we can't really cancel cleanly
|
||||
// here. If we don't process this runnable then we will leave something
|
||||
// unprocessed in the message_loop. Therefore, eagerly complete our work
|
||||
// instead by immediately calling Run(). Run() should be called separately
|
||||
// after this. Unfortunately we cannot use flags to verify this because
|
||||
// DoWorkRunnable is a stateless singleton that can be in the event queue
|
||||
// multiple times simultaneously.
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user