зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1436784 - Implement WeakWorkerRef, StrongWorkerRef and ThreadSafeWorkerRef, r=smaug
This commit is contained in:
Родитель
c887fd120c
Коммит
8d4bb177fa
|
@ -59,9 +59,7 @@
|
|||
#include "WorkerScope.h"
|
||||
#include "WorkerThread.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "nsThreadManager.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
|
@ -5200,10 +5198,8 @@ WorkerPrivate::CreateDebuggerGlobalScope(JSContext* aCx)
|
|||
return mDebuggerScope;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
void
|
||||
WorkerPrivate::AssertIsOnWorkerThread() const
|
||||
bool
|
||||
WorkerPrivate::IsOnWorkerThread() const
|
||||
{
|
||||
// This is much more complicated than it needs to be but we can't use mThread
|
||||
// because it must be protected by mMutex and sometimes this method is called
|
||||
|
@ -5220,10 +5216,15 @@ WorkerPrivate::AssertIsOnWorkerThread() const
|
|||
|
||||
bool current;
|
||||
rv = thread->IsOnCurrentThread(¤t);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
MOZ_ASSERT(current, "Wrong thread!");
|
||||
return NS_SUCCEEDED(rv) && current;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
WorkerPrivate::AssertIsOnWorkerThread() const
|
||||
{
|
||||
MOZ_ASSERT(IsOnWorkerThread());
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void
|
||||
|
|
|
@ -421,6 +421,9 @@ public:
|
|||
void
|
||||
SetThread(WorkerThread* aThread);
|
||||
|
||||
bool
|
||||
IsOnWorkerThread() const;
|
||||
|
||||
void
|
||||
AssertIsOnWorkerThread() const
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "WorkerRef.h"
|
||||
|
||||
#include "mozilla/Unused.h"
|
||||
#include "WorkerHolder.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
// This runnable is used to release the StrongWorkerRef on the worker thread
|
||||
// when a ThreadSafeWorkerRef is released.
|
||||
class ReleaseRefControlRunnable final : public WorkerControlRunnable
|
||||
{
|
||||
public:
|
||||
ReleaseRefControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<StrongWorkerRef> aRef)
|
||||
: WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount)
|
||||
, mRef(Move(aRef))
|
||||
{
|
||||
MOZ_ASSERT(mRef);
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) override
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
mRef = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<StrongWorkerRef> mRef;
|
||||
};
|
||||
|
||||
} // anonymous
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// WorkerRef::Holder
|
||||
|
||||
class WorkerRef::Holder final : public mozilla::dom::WorkerHolder
|
||||
{
|
||||
public:
|
||||
Holder(const char* aName, WorkerRef* aWorkerRef, Behavior aBehavior)
|
||||
: mozilla::dom::WorkerHolder(aName, aBehavior)
|
||||
, mWorkerRef(aWorkerRef)
|
||||
{}
|
||||
|
||||
bool
|
||||
Notify(WorkerStatus aStatus) override
|
||||
{
|
||||
mWorkerRef->Notify();
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
WorkerRef* mWorkerRef;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// WorkerRef
|
||||
|
||||
WorkerRef::WorkerRef(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
WorkerRef::~WorkerRef()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(WorkerRef);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerRef::Notify()
|
||||
{
|
||||
MOZ_ASSERT(mHolder);
|
||||
NS_ASSERT_OWNINGTHREAD(WorkerRef);
|
||||
|
||||
mHolder = nullptr;
|
||||
|
||||
if (!mCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::function<void()> callback = mCallback;
|
||||
mCallback = nullptr;
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// WeakWorkerRef
|
||||
|
||||
/* static */ already_AddRefed<WeakWorkerRef>
|
||||
WeakWorkerRef::Create(WorkerPrivate* aWorkerPrivate,
|
||||
const std::function<void()>& aCallback)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<WeakWorkerRef> ref = new WeakWorkerRef(aWorkerPrivate);
|
||||
|
||||
// This holder doesn't keep the worker alive.
|
||||
UniquePtr<Holder> holder(new Holder("WeakWorkerRef::Holder", ref,
|
||||
WorkerHolder::AllowIdleShutdownStart));
|
||||
if (NS_WARN_IF(!holder->HoldWorker(aWorkerPrivate, Closing))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ref->mHolder = Move(holder);
|
||||
ref->mCallback = aCallback;
|
||||
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
WeakWorkerRef::WeakWorkerRef(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerRef(aWorkerPrivate)
|
||||
{}
|
||||
|
||||
WeakWorkerRef::~WeakWorkerRef() = default;
|
||||
|
||||
void
|
||||
WeakWorkerRef::Notify()
|
||||
{
|
||||
WorkerRef::Notify();
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
WeakWorkerRef::GetPrivate() const
|
||||
{
|
||||
MOZ_ASSERT(mHolder);
|
||||
NS_ASSERT_OWNINGTHREAD(WeakWorkerRef);
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// StrongWorkerRef
|
||||
|
||||
/* static */ already_AddRefed<StrongWorkerRef>
|
||||
StrongWorkerRef::Create(WorkerPrivate* aWorkerPrivate,
|
||||
const char* aName,
|
||||
const std::function<void()>& aCallback)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aName);
|
||||
|
||||
RefPtr<StrongWorkerRef> ref = new StrongWorkerRef(aWorkerPrivate);
|
||||
|
||||
// The worker is kept alive by this holder.
|
||||
UniquePtr<Holder> holder(new Holder(aName, ref,
|
||||
WorkerHolder::PreventIdleShutdownStart));
|
||||
if (NS_WARN_IF(!holder->HoldWorker(aWorkerPrivate, Closing))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ref->mHolder = Move(holder);
|
||||
ref->mCallback = aCallback;
|
||||
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
StrongWorkerRef::StrongWorkerRef(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerRef(aWorkerPrivate)
|
||||
{}
|
||||
|
||||
StrongWorkerRef::~StrongWorkerRef() = default;
|
||||
|
||||
WorkerPrivate*
|
||||
StrongWorkerRef::Private() const
|
||||
{
|
||||
MOZ_ASSERT(mHolder);
|
||||
NS_ASSERT_OWNINGTHREAD(StrongWorkerRef);
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
ThreadSafeWorkerRef::ThreadSafeWorkerRef(StrongWorkerRef* aRef)
|
||||
: mRef(aRef)
|
||||
{
|
||||
MOZ_ASSERT(aRef);
|
||||
aRef->Private()->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
ThreadSafeWorkerRef::~ThreadSafeWorkerRef()
|
||||
{
|
||||
// Let's release the StrongWorkerRef on the correct thread.
|
||||
if (!mRef->mWorkerPrivate->IsOnWorkerThread()) {
|
||||
RefPtr<ReleaseRefControlRunnable> r =
|
||||
new ReleaseRefControlRunnable(mRef->mWorkerPrivate, mRef.forget());
|
||||
r->Dispatch();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
ThreadSafeWorkerRef::Private() const
|
||||
{
|
||||
return mRef->mWorkerPrivate;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
|
@ -0,0 +1,166 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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_dom_workers_WorkerRef_h
|
||||
#define mozilla_dom_workers_WorkerRef_h
|
||||
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/dom/WorkerHolder.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* If you want to play with a DOM Worker, you must know that it can go away
|
||||
* at any time if nothing prevents its shutting down. This documentation helps
|
||||
* to understand how to play with DOM Workers correctly.
|
||||
*
|
||||
* There are several reasons why a DOM Worker could go away. Here is the
|
||||
* complete list:
|
||||
*
|
||||
* a. GC/CC - If the DOM Worker thread is idle and the Worker object is garbage
|
||||
* collected, it goes away.
|
||||
* b. The worker script can call self.close()
|
||||
* c. The Worker object calls worker.terminate()
|
||||
* d. Firefox is shutting down.
|
||||
*
|
||||
* When a DOM Worker goes away, it does several steps. See more in
|
||||
* WorkerHolder.h. The DOM Worker thread will basically stop scheduling
|
||||
* WorkerRunnables, and eventually WorkerControlRunnables. But if there is
|
||||
* something preventing the shutting down, it will always possible to dispatch
|
||||
* WorkerControlRunnables. Of course, at some point, the worker _must_ be
|
||||
* released, otherwise firefox will leak it and the browser shutdown will hang.
|
||||
*
|
||||
* WeakWorkerRef is a refcounted, NON thread-safe object.
|
||||
*
|
||||
* From this object, you can obtain a WorkerPrivate, calling
|
||||
* WeakWorkerRef::GetPrivate(). It returns nullptr if the worker is shutting
|
||||
* down or if it is already gone away.
|
||||
*
|
||||
* If you want to know when a DOM Worker starts the shutting down procedure,
|
||||
* pass a callback to the mozilla::dom::WeakWorkerRef::Create() method.
|
||||
* Your function will be called. Note that _after_ the callback,
|
||||
* WeakWorkerRef::GetPrivate() will return nullptr.
|
||||
*
|
||||
* How to keep a DOM Worker alive?
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* If you need to keep the worker alive, you must use StrongWorkerRef.
|
||||
* You can have this refcounted, NON thread-safe object, calling
|
||||
* mozilla::dom::StrongWorkerRef::Create(WorkerPrivate* aWorkerPrivate);
|
||||
*
|
||||
* If you have a StrongWorkerRef:
|
||||
* a. the DOM Worker is kept alive.
|
||||
* b. you can have access to the WorkerPrivate, calling: Private().
|
||||
* c. WorkerControlRunnable can be dispatched.
|
||||
*
|
||||
* Note that the DOM Worker shutdown can start at any time, but having a
|
||||
* StrongWorkerRef prevents the full shutdown. Also with StrongWorkerRef, you
|
||||
* can pass a callback when calling mozilla::dom::StrongWorkerRef::Create().
|
||||
*
|
||||
* When the DOM Worker shutdown starts, WorkerRunnable cannot be dispatched
|
||||
* anymore. At this point, you should dispatch WorkerControlRunnable just to
|
||||
* release resources.
|
||||
*
|
||||
* How to have a thread-safe DOM Worker reference?
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sometimes you need to play with threads and you need a thread-safe worker
|
||||
* reference. ThreadSafeWorkerRef is what you want.
|
||||
*
|
||||
* Just because this object can be sent to different threads, we don't allow the
|
||||
* setting of a callback. It would be confusing.
|
||||
*
|
||||
* ThreadSafeWorkerRef can be destroyed in any thread. Internally it keeps a
|
||||
* reference to its StrongWorkerRef creator and this ref will be dropped on the
|
||||
* correct thread when the ThreadSafeWorkerRef is deleted.
|
||||
*/
|
||||
|
||||
class WorkerPrivate;
|
||||
class StrongWorkerRef;
|
||||
class ThreadSafeWorkerRef;
|
||||
|
||||
class WorkerRef
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(WorkerRef)
|
||||
|
||||
protected:
|
||||
class Holder;
|
||||
friend class Holder;
|
||||
|
||||
explicit WorkerRef(WorkerPrivate* aWorkerPrivate);
|
||||
virtual ~WorkerRef();
|
||||
|
||||
virtual void
|
||||
Notify();
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
UniquePtr<WorkerHolder> mHolder;
|
||||
|
||||
std::function<void()> mCallback;
|
||||
};
|
||||
|
||||
class WeakWorkerRef final : public WorkerRef
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<WeakWorkerRef>
|
||||
Create(WorkerPrivate* aWorkerPrivate,
|
||||
const std::function<void()>& aCallback = nullptr);
|
||||
|
||||
WorkerPrivate*
|
||||
GetPrivate() const;
|
||||
|
||||
private:
|
||||
explicit WeakWorkerRef(WorkerPrivate* aWorkerPrivate);
|
||||
~WeakWorkerRef();
|
||||
|
||||
void
|
||||
Notify() override;
|
||||
};
|
||||
|
||||
class StrongWorkerRef final : public WorkerRef
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<StrongWorkerRef>
|
||||
Create(WorkerPrivate* aWorkerPrivate,
|
||||
const char* aName,
|
||||
const std::function<void()>& aCallback = nullptr);
|
||||
|
||||
WorkerPrivate*
|
||||
Private() const;
|
||||
|
||||
private:
|
||||
friend class WeakWorkerRef;
|
||||
friend class ThreadSafeWorkerRef;
|
||||
|
||||
explicit StrongWorkerRef(WorkerPrivate* aWorkerPrivate);
|
||||
~StrongWorkerRef();
|
||||
};
|
||||
|
||||
class ThreadSafeWorkerRef final
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSafeWorkerRef)
|
||||
|
||||
explicit ThreadSafeWorkerRef(StrongWorkerRef* aRef);
|
||||
|
||||
WorkerPrivate*
|
||||
Private() const;
|
||||
|
||||
private:
|
||||
friend class StrongWorkerRef;
|
||||
|
||||
~ThreadSafeWorkerRef();
|
||||
|
||||
RefPtr<StrongWorkerRef> mRef;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif /* mozilla_dom_workers_WorkerRef_h */
|
|
@ -21,6 +21,7 @@ EXPORTS.mozilla.dom += [
|
|||
'WorkerLocation.h',
|
||||
'WorkerNavigator.h',
|
||||
'WorkerPrivate.h',
|
||||
'WorkerRef.h',
|
||||
'WorkerRunnable.h',
|
||||
'WorkerScope.h',
|
||||
]
|
||||
|
@ -59,6 +60,7 @@ UNIFIED_SOURCES += [
|
|||
'WorkerLocation.cpp',
|
||||
'WorkerNavigator.cpp',
|
||||
'WorkerPrivate.cpp',
|
||||
'WorkerRef.cpp',
|
||||
'WorkerRunnable.cpp',
|
||||
'WorkerScope.cpp',
|
||||
'WorkerThread.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче