summaryrefslogtreecommitdiffstats
path: root/dom/workers/WorkerRef.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/WorkerRef.h')
-rw-r--r--dom/workers/WorkerRef.h276
1 files changed, 276 insertions, 0 deletions
diff --git a/dom/workers/WorkerRef.h b/dom/workers/WorkerRef.h
new file mode 100644
index 0000000000..e41ef07bfd
--- /dev/null
+++ b/dom/workers/WorkerRef.h
@@ -0,0 +1,276 @@
+/* -*- 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/WorkerStatus.h"
+#include "mozilla/MoveOnlyFunction.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupports.h"
+#include "nsTString.h"
+
+#ifdef DEBUG
+# include "mozilla/Mutex.h"
+#endif
+
+namespace mozilla::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
+ * WorkerStatus.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.
+ *
+ * IPC WorkerRef
+ * ~~~~~~~~~~~~~
+ *
+ * IPDL protocols require a correct shutdown sequence. Because of this, they
+ * need a special configuration:
+ * 1. they need to be informed when the Worker starts the shutting down
+ * 2. they don't want to prevent the shutdown
+ * 3. but at the same time, they need to block the shutdown until the WorkerRef
+ * is not longer alive.
+ *
+ * Point 1 is a standard feature of WorkerRef; point 2 is similar to
+ * WeakWorkerRef; point 3 is similar to StrongWorkerRef.
+ *
+ * You can create a special IPC WorkerRef using this static method:
+ * mozilla::dom::IPCWorkerRef::Create(WorkerPrivate* aWorkerPrivate,
+ * const char* * aName);
+ */
+
+class WorkerPrivate;
+class StrongWorkerRef;
+class ThreadSafeWorkerRef;
+
+#ifdef DEBUG // In debug mode, provide a way for clients to annotate WorkerRefs
+# define SET_WORKERREF_DEBUG_STATUS(workerref, str) \
+ ((workerref)->DebugSetWorkerRefStatus(str))
+# define GET_WORKERREF_DEBUG_STATUS(workerref) \
+ ((workerref)->DebugGetWorkerRefStatus())
+#else
+# define SET_WORKERREF_DEBUG_STATUS(workerref, str) (void())
+# define GET_WORKERREF_DEBUG_STATUS(workerref) (EmptyCString())
+#endif
+
+class WorkerRef {
+ friend class WorkerPrivate;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(WorkerRef)
+
+#ifdef DEBUG
+ mutable Mutex mDebugMutex;
+ nsCString mDebugStatus MOZ_GUARDED_BY(mDebugMutex);
+
+ void DebugSetWorkerRefStatus(const nsCString& aStatus) {
+ MutexAutoLock lock(mDebugMutex);
+ mDebugStatus = aStatus;
+ }
+
+ const nsCString DebugGetWorkerRefStatus() const {
+ MutexAutoLock lock(mDebugMutex);
+ return mDebugStatus;
+ }
+#endif
+
+ protected:
+ WorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName,
+ bool aIsPreventingShutdown);
+ virtual ~WorkerRef();
+
+ virtual void Notify();
+
+ bool HoldWorker(WorkerStatus aStatus);
+ void ReleaseWorker();
+
+ bool IsPreventingShutdown() const { return mIsPreventingShutdown; }
+
+ const char* Name() const { return mName; }
+
+ WorkerPrivate* mWorkerPrivate;
+
+ MoveOnlyFunction<void()> mCallback;
+ const char* const mName;
+ const bool mIsPreventingShutdown;
+
+ // True if this WorkerRef has been added to a WorkerPrivate.
+ bool mHolding;
+};
+
+class WeakWorkerRef final : public WorkerRef {
+ public:
+ static already_AddRefed<WeakWorkerRef> Create(
+ WorkerPrivate* aWorkerPrivate,
+ MoveOnlyFunction<void()>&& aCallback = nullptr);
+
+ WorkerPrivate* GetPrivate() const;
+
+ // This can be called on any thread. It's racy and, in general, the wrong
+ // choice.
+ WorkerPrivate* GetUnsafePrivate() 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,
+ MoveOnlyFunction<void()>&& aCallback = nullptr);
+
+ // This function creates a StrongWorkerRef even when in the Canceling state of
+ // the worker's lifecycle. It's intended to be used by system code, e.g. code
+ // that needs to perform IPC.
+ //
+ // This method should only be used in cases where the StrongWorkerRef will be
+ // used for an extremely bounded duration that cannot be impacted by content.
+ // For example, IPCStreams use this type of ref in order to immediately
+ // migrate to an actor on another thread. Whether the IPCStream ever actually
+ // is streamed does not matter; the ref will be dropped once the new actor is
+ // created. For this reason, this method does not take a callback. It's
+ // expected and required that callers will drop the reference when they are
+ // done.
+ static already_AddRefed<StrongWorkerRef> CreateForcibly(
+ WorkerPrivate* aWorkerPrivate, const char* aName);
+
+ WorkerPrivate* Private() const;
+
+ private:
+ friend class WeakWorkerRef;
+ friend class ThreadSafeWorkerRef;
+
+ static already_AddRefed<StrongWorkerRef> CreateImpl(
+ WorkerPrivate* aWorkerPrivate, const char* aName,
+ WorkerStatus aFailStatus);
+
+ StrongWorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName);
+ ~StrongWorkerRef();
+};
+
+class ThreadSafeWorkerRef final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSafeWorkerRef)
+
+ explicit ThreadSafeWorkerRef(StrongWorkerRef* aRef);
+
+ WorkerPrivate* Private() const;
+
+#ifdef DEBUG
+ RefPtr<StrongWorkerRef>& Ref() { return mRef; }
+#endif
+
+ private:
+ friend class StrongWorkerRef;
+
+ ~ThreadSafeWorkerRef();
+
+ RefPtr<StrongWorkerRef> mRef;
+};
+
+class IPCWorkerRef final : public WorkerRef {
+ public:
+ static already_AddRefed<IPCWorkerRef> Create(
+ WorkerPrivate* aWorkerPrivate, const char* aName,
+ MoveOnlyFunction<void()>&& aCallback = nullptr);
+
+ WorkerPrivate* Private() const;
+
+ void SetActorCount(uint32_t aCount);
+
+ private:
+ IPCWorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName);
+ ~IPCWorkerRef();
+
+ // The count of background actors which binding with this IPCWorkerRef.
+ uint32_t mActorCount;
+};
+
+// Template class to keep an Actor pointer, as a raw pointer, in a ref-counted
+// way when passed to lambdas.
+template <class ActorPtr>
+class IPCWorkerRefHelper final {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(IPCWorkerRefHelper);
+
+ explicit IPCWorkerRefHelper(ActorPtr* aActor) : mActor(aActor) {}
+
+ ActorPtr* Actor() const { return mActor; }
+
+ private:
+ ~IPCWorkerRefHelper() = default;
+
+ // Raw pointer
+ ActorPtr* mActor;
+};
+
+} // namespace mozilla::dom
+
+#endif /* mozilla_dom_workers_WorkerRef_h */