tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WorkerRef.h (9180B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_dom_workers_WorkerRef_h
      8 #define mozilla_dom_workers_WorkerRef_h
      9 
     10 #include "mozilla/MoveOnlyFunction.h"
     11 #include "mozilla/RefPtr.h"
     12 #include "mozilla/dom/WorkerStatus.h"
     13 #include "nsISupports.h"
     14 #include "nsTString.h"
     15 
     16 #ifdef DEBUG
     17 #  include "mozilla/Mutex.h"
     18 #endif
     19 
     20 namespace mozilla::dom {
     21 
     22 /*
     23 * If you want to play with a DOM Worker, you must know that it can go away
     24 * at any time if nothing prevents its shutting down. This documentation helps
     25 * to understand how to play with DOM Workers correctly.
     26 *
     27 * There are several reasons why a DOM Worker could go away. Here is the
     28 * complete list:
     29 *
     30 * a. GC/CC - If the DOM Worker thread is idle and the Worker object is garbage
     31 *    collected, it goes away.
     32 * b. The worker script can call self.close()
     33 * c. The Worker object calls worker.terminate()
     34 * d. Firefox is shutting down.
     35 *
     36 * When a DOM Worker goes away, it does several steps. See more in
     37 * WorkerStatus.h. The DOM Worker thread will basically stop scheduling
     38 * WorkerRunnables, and eventually WorkerControlRunnables. But if there is
     39 * something preventing the shutting down, it will always possible to dispatch
     40 * WorkerControlRunnables.  Of course, at some point, the worker _must_ be
     41 * released, otherwise firefox will leak it and the browser shutdown will hang.
     42 *
     43 * WeakWorkerRef is a refcounted, NON thread-safe object.
     44 *
     45 * From this object, you can obtain a WorkerPrivate, calling
     46 * WeakWorkerRef::GetPrivate(). It returns nullptr if the worker is shutting
     47 * down or if it is already gone away.
     48 *
     49 * If you want to know when a DOM Worker starts the shutting down procedure,
     50 * pass a callback to the mozilla::dom::WeakWorkerRef::Create() method.
     51 * Your function will be called. Note that _after_ the callback,
     52 * WeakWorkerRef::GetPrivate() will return nullptr.
     53 *
     54 * How to keep a DOM Worker alive?
     55 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     56 *
     57 * If you need to keep the worker alive, you must use StrongWorkerRef.
     58 * You can have this refcounted, NON thread-safe object, calling
     59 * mozilla::dom::StrongWorkerRef::Create(WorkerPrivate* aWorkerPrivate);
     60 *
     61 * If you have a StrongWorkerRef:
     62 * a. the DOM Worker is kept alive.
     63 * b. you can have access to the WorkerPrivate, calling: Private().
     64 * c. WorkerControlRunnable can be dispatched.
     65 *
     66 * Note that the DOM Worker shutdown can start at any time, but having a
     67 * StrongWorkerRef prevents the full shutdown. Also with StrongWorkerRef, you
     68 * can pass a callback when calling mozilla::dom::StrongWorkerRef::Create().
     69 *
     70 * When the DOM Worker shutdown starts, WorkerRunnable cannot be dispatched
     71 * anymore. At this point, you should dispatch WorkerControlRunnable just to
     72 * release resources.
     73 *
     74 * How to have a thread-safe DOM Worker reference?
     75 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     76 *
     77 * Sometimes you need to play with threads and you need a thread-safe worker
     78 * reference. ThreadSafeWorkerRef is what you want.
     79 *
     80 * Just because this object can be sent to different threads, we don't allow the
     81 * setting of a callback. It would be confusing.
     82 *
     83 * ThreadSafeWorkerRef can be destroyed in any thread. Internally it keeps a
     84 * reference to its StrongWorkerRef creator and this ref will be dropped on the
     85 * correct thread when the ThreadSafeWorkerRef is deleted.
     86 *
     87 * IPC WorkerRef
     88 * ~~~~~~~~~~~~~
     89 *
     90 * IPDL protocols require a correct shutdown sequence. Because of this, they
     91 * need a special configuration:
     92 * 1. they need to be informed when the Worker starts the shutting down
     93 * 2. they don't want to prevent the shutdown
     94 * 3. but at the same time, they need to block the shutdown until the WorkerRef
     95 *    is not longer alive.
     96 *
     97 * Point 1 is a standard feature of WorkerRef; point 2 is similar to
     98 * WeakWorkerRef; point 3 is similar to StrongWorkerRef.
     99 *
    100 * You can create a special IPC WorkerRef using this static method:
    101 * mozilla::dom::IPCWorkerRef::Create(WorkerPrivate* aWorkerPrivate,
    102 *                                    const char* * aName);
    103 */
    104 
    105 class WorkerPrivate;
    106 class StrongWorkerRef;
    107 class ThreadSafeWorkerRef;
    108 
    109 #ifdef DEBUG  // In debug mode, provide a way for clients to annotate WorkerRefs
    110 #  define SET_WORKERREF_DEBUG_STATUS(workerref, str) \
    111    ((workerref)->DebugSetWorkerRefStatus(str))
    112 #  define GET_WORKERREF_DEBUG_STATUS(workerref) \
    113    ((workerref)->DebugGetWorkerRefStatus())
    114 #else
    115 #  define SET_WORKERREF_DEBUG_STATUS(workerref, str) (void())
    116 #  define GET_WORKERREF_DEBUG_STATUS(workerref) (EmptyCString())
    117 #endif
    118 
    119 class WorkerRef {
    120  friend class WorkerPrivate;
    121 
    122 public:
    123  NS_INLINE_DECL_REFCOUNTING(WorkerRef)
    124 
    125 #ifdef DEBUG
    126  mutable Mutex mDebugMutex;
    127  nsCString mDebugStatus MOZ_GUARDED_BY(mDebugMutex);
    128 
    129  void DebugSetWorkerRefStatus(const nsCString& aStatus) {
    130    MutexAutoLock lock(mDebugMutex);
    131    mDebugStatus = aStatus;
    132  }
    133 
    134  const nsCString DebugGetWorkerRefStatus() const {
    135    MutexAutoLock lock(mDebugMutex);
    136    return mDebugStatus;
    137  }
    138 #endif
    139 
    140 protected:
    141  WorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName,
    142            bool aIsPreventingShutdown);
    143  virtual ~WorkerRef();
    144 
    145  virtual void Notify();
    146 
    147  bool HoldWorker(WorkerStatus aStatus);
    148  void ReleaseWorker();
    149 
    150  bool IsPreventingShutdown() const { return mIsPreventingShutdown; }
    151 
    152  const char* Name() const { return mName; }
    153 
    154  WorkerPrivate* mWorkerPrivate;
    155 
    156  MoveOnlyFunction<void()> mCallback;
    157  const char* const mName;
    158  const bool mIsPreventingShutdown;
    159 
    160  // True if this WorkerRef has been added to a WorkerPrivate.
    161  bool mHolding;
    162 };
    163 
    164 class WeakWorkerRef final : public WorkerRef {
    165 public:
    166  static already_AddRefed<WeakWorkerRef> Create(
    167      WorkerPrivate* aWorkerPrivate,
    168      MoveOnlyFunction<void()>&& aCallback = nullptr);
    169 
    170  WorkerPrivate* GetPrivate() const;
    171 
    172  // This can be called on any thread. It's racy and, in general, the wrong
    173  // choice.
    174  WorkerPrivate* GetUnsafePrivate() const;
    175 
    176 private:
    177  friend class ThreadSafeWeakWorkerRef;
    178 
    179  explicit WeakWorkerRef(WorkerPrivate* aWorkerPrivate);
    180  ~WeakWorkerRef();
    181 
    182  void Notify() override;
    183 };
    184 
    185 class StrongWorkerRef final : public WorkerRef {
    186 public:
    187  static already_AddRefed<StrongWorkerRef> Create(
    188      WorkerPrivate* aWorkerPrivate, const char* aName,
    189      MoveOnlyFunction<void()>&& aCallback = nullptr);
    190 
    191  // This function creates a StrongWorkerRef even when in the Canceling state of
    192  // the worker's lifecycle. It's intended to be used by system code, e.g. code
    193  // that needs to perform IPC.
    194  //
    195  // This method should only be used in cases where the StrongWorkerRef will be
    196  // used for an extremely bounded duration that cannot be impacted by content.
    197  // For example, IPCStreams use this type of ref in order to immediately
    198  // migrate to an actor on another thread. Whether the IPCStream ever actually
    199  // is streamed does not matter; the ref will be dropped once the new actor is
    200  // created. For this reason, this method does not take a callback. It's
    201  // expected and required that callers will drop the reference when they are
    202  // done.
    203  static already_AddRefed<StrongWorkerRef> CreateForcibly(
    204      WorkerPrivate* aWorkerPrivate, const char* aName);
    205 
    206  WorkerPrivate* Private() const;
    207 
    208 private:
    209  friend class WeakWorkerRef;
    210  friend class ThreadSafeWorkerRef;
    211 
    212  static already_AddRefed<StrongWorkerRef> CreateImpl(
    213      WorkerPrivate* aWorkerPrivate, const char* aName,
    214      WorkerStatus aFailStatus);
    215 
    216  StrongWorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName);
    217  ~StrongWorkerRef();
    218 };
    219 
    220 class ThreadSafeWorkerRef final {
    221 public:
    222  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSafeWorkerRef)
    223 
    224  explicit ThreadSafeWorkerRef(StrongWorkerRef* aRef);
    225 
    226  WorkerPrivate* Private() const;
    227 
    228 #ifdef DEBUG
    229  RefPtr<StrongWorkerRef>& Ref() { return mRef; }
    230 #endif
    231 
    232 private:
    233  friend class StrongWorkerRef;
    234 
    235  ~ThreadSafeWorkerRef();
    236 
    237  RefPtr<StrongWorkerRef> mRef;
    238 };
    239 
    240 class IPCWorkerRef final : public WorkerRef {
    241 public:
    242  static already_AddRefed<IPCWorkerRef> Create(
    243      WorkerPrivate* aWorkerPrivate, const char* aName,
    244      MoveOnlyFunction<void()>&& aCallback = nullptr);
    245 
    246  WorkerPrivate* Private() const;
    247 
    248  void SetActorCount(uint32_t aCount);
    249 
    250 private:
    251  IPCWorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName);
    252  ~IPCWorkerRef();
    253 
    254  // The count of background actors which binding with this IPCWorkerRef.
    255  uint32_t mActorCount;
    256 };
    257 
    258 // Template class to keep an Actor pointer, as a raw pointer, in a ref-counted
    259 // way when passed to lambdas.
    260 template <class ActorPtr>
    261 class IPCWorkerRefHelper final {
    262 public:
    263  NS_INLINE_DECL_REFCOUNTING(IPCWorkerRefHelper);
    264 
    265  explicit IPCWorkerRefHelper(ActorPtr* aActor) : mActor(aActor) {}
    266 
    267  ActorPtr* Actor() const { return mActor; }
    268 
    269 private:
    270  ~IPCWorkerRefHelper() = default;
    271 
    272  // Raw pointer
    273  ActorPtr* mActor;
    274 };
    275 
    276 }  // namespace mozilla::dom
    277 
    278 #endif /* mozilla_dom_workers_WorkerRef_h */