DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (1aeaa33a64f9)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
/* -*- 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_PromiseWorkerProxy_h
#define mozilla_dom_PromiseWorkerProxy_h

// Required for Promise::PromiseTaskSync.
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/StructuredCloneHolder.h"
#include "mozilla/dom/WorkerHolder.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "nsProxyRelease.h"

namespace mozilla {
namespace dom {

class Promise;
class WorkerPrivate;

// A proxy to (eventually) mirror a resolved/rejected Promise's result from the
// main thread to a Promise on the worker thread.
//
// How to use:
//
//   1. Create a Promise on the worker thread and return it to the content
//      script:
//
//        RefPtr<Promise> promise =
//          Promise::Create(workerPrivate->GlobalScope(), aRv);
//        if (aRv.Failed()) {
//          return nullptr;
//        }
//
//   2. Create a PromiseWorkerProxy wrapping the Promise. If this fails, the
//      worker is shutting down and you should fail the original call. This is
//      only likely to happen in (Gecko-specific) worker onclose handlers.
//
//        RefPtr<PromiseWorkerProxy> proxy =
//          PromiseWorkerProxy::Create(workerPrivate, promise);
//        if (!proxy) {
//          // You may also reject the Promise with an AbortError or similar.
//          return nullptr;
//        }
//
//   3. Dispatch a runnable to the main thread, with a reference to the proxy to
//      perform the main thread operation. PromiseWorkerProxy is thread-safe
//      refcounted.
//
//   4. Return the worker thread promise to the JS caller:
//
//        return promise.forget();
//
//   5. In your main thread runnable Run(), obtain a Promise on
//      the main thread and call its AppendNativeHandler(PromiseNativeHandler*)
//      to bind the PromiseWorkerProxy created at #2.
//
//   4. Then the Promise results returned by ResolvedCallback/RejectedCallback
//      will be dispatched as a WorkerRunnable to the worker thread to
//      resolve/reject the Promise created at #1.
//
// PromiseWorkerProxy can also be used in situations where there is no main
// thread Promise, or where special handling is required on the worker thread
// for promise resolution. Create a PromiseWorkerProxy as in steps 1 to 3
// above. When the main thread is ready to resolve the worker thread promise:
//
//   1. Acquire the mutex before attempting to access the worker private.
//
//        AssertIsOnMainThread();
//        MutexAutoLock lock(proxy->Lock());
//        if (proxy->CleanedUp()) {
//          // Worker has already shut down, can't access worker private.
//          return;
//        }
//
//   2. Dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
//      worker.
//
//        RefPtr<FinishTaskWorkerRunnable> runnable =
//          new FinishTaskWorkerRunnable(proxy->GetWorkerPrivate(), proxy,
//                                       result);
//        if (!r->Dispatch()) {
//          // Worker is alive but not Running any more, so the Promise can't
//          // be resolved, give up. The proxy will get Release()d at some
//          // point.
//
//          // Usually do nothing, but you may want to log the fact.
//        }
//
//   3. In the WorkerRunnable's WorkerRun() use WorkerPromise() to access the
//      Promise and resolve/reject it. Then call CleanUp().
//
//        bool
//        WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
//        {
//          aWorkerPrivate->AssertIsOnWorkerThread();
//          RefPtr<Promise> promise = mProxy->WorkerPromise();
//          promise->MaybeResolve(mResult);
//          mProxy->CleanUp();
//        }
//
// Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
// can happen if the main thread Promise is never fulfilled - it will
// stay alive till the worker reaches a Canceling state, even if all external
// references to it are dropped.

class PromiseWorkerProxy : public PromiseNativeHandler,
                           public StructuredCloneHolderBase {
  friend class PromiseWorkerProxyRunnable;

  NS_DECL_THREADSAFE_ISUPPORTS

 public:
  typedef JSObject* (*ReadCallbackOp)(JSContext* aCx,
                                      JSStructuredCloneReader* aReader,
                                      const PromiseWorkerProxy* aProxy,
                                      uint32_t aTag, uint32_t aData);
  typedef bool (*WriteCallbackOp)(JSContext* aCx,
                                  JSStructuredCloneWriter* aWorker,
                                  PromiseWorkerProxy* aProxy,
                                  JS::HandleObject aObj);

  struct PromiseWorkerProxyStructuredCloneCallbacks {
    ReadCallbackOp Read;
    WriteCallbackOp Write;
  };

  static already_AddRefed<PromiseWorkerProxy> Create(
      WorkerPrivate* aWorkerPrivate, Promise* aWorkerPromise,
      const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);

  // Main thread callers must hold Lock() and check CleanUp() before calling
  // this. Worker thread callers, this will assert that the proxy has not been
  // cleaned up.
  WorkerPrivate* GetWorkerPrivate() const;

  // This should only be used within WorkerRunnable::WorkerRun() running on the
  // worker thread! Do not call this after calling CleanUp().
  Promise* WorkerPromise() const;

  // Worker thread only. Calling this invalidates several assumptions, so be
  // sure this is the last thing you do.
  // 1. WorkerPrivate() will no longer return a valid worker.
  // 2. WorkerPromise() will crash!
  void CleanUp();

  Mutex& Lock() { return mCleanUpLock; }

  bool CleanedUp() const {
    mCleanUpLock.AssertCurrentThreadOwns();
    return mCleanedUp;
  }

  // StructuredCloneHolderBase

  JSObject* CustomReadHandler(JSContext* aCx, JSStructuredCloneReader* aReader,
                              uint32_t aTag, uint32_t aIndex) override;

  bool CustomWriteHandler(JSContext* aCx, JSStructuredCloneWriter* aWriter,
                          JS::Handle<JSObject*> aObj) override;

 protected:
  virtual void ResolvedCallback(JSContext* aCx,
                                JS::Handle<JS::Value> aValue) override;

  virtual void RejectedCallback(JSContext* aCx,
                                JS::Handle<JS::Value> aValue) override;

 private:
  PromiseWorkerProxy(
      WorkerPrivate* aWorkerPrivate, Promise* aWorkerPromise,
      const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);

  virtual ~PromiseWorkerProxy();

  bool AddRefObject();

  // If not called from Create(), be sure to hold Lock().
  void CleanProperties();

  // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
  typedef void (Promise::*RunCallbackFunc)(JSContext*, JS::Handle<JS::Value>);

  void RunCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                   RunCallbackFunc aFunc);

  // Any thread with appropriate checks.
  WorkerPrivate* mWorkerPrivate;

  // Worker thread only.
  RefPtr<Promise> mWorkerPromise;

  // Modified on the worker thread.
  // It is ok to *read* this without a lock on the worker.
  // Main thread must always acquire a lock.
  bool mCleanedUp;  // To specify if the cleanUp() has been done.

  const PromiseWorkerProxyStructuredCloneCallbacks* mCallbacks;

  // Ensure the worker and the main thread won't race to access |mCleanedUp|.
  Mutex mCleanUpLock;

  UniquePtr<WorkerHolder> mWorkerHolder;
};
}  // namespace dom
}  // namespace mozilla

#endif  // mozilla_dom_PromiseWorkerProxy_h