PromiseWorkerProxy.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #ifndef mozilla_dom_PromiseWorkerProxy_h
  6. #define mozilla_dom_PromiseWorkerProxy_h
  7. // Required for Promise::PromiseTaskSync.
  8. #include "mozilla/dom/Promise.h"
  9. #include "mozilla/dom/PromiseNativeHandler.h"
  10. #include "mozilla/dom/StructuredCloneHolder.h"
  11. #include "mozilla/dom/workers/bindings/WorkerHolder.h"
  12. #include "nsProxyRelease.h"
  13. #include "WorkerRunnable.h"
  14. namespace mozilla {
  15. namespace dom {
  16. class Promise;
  17. namespace workers {
  18. class WorkerPrivate;
  19. } // namespace workers
  20. // A proxy to (eventually) mirror a resolved/rejected Promise's result from the
  21. // main thread to a Promise on the worker thread.
  22. //
  23. // How to use:
  24. //
  25. // 1. Create a Promise on the worker thread and return it to the content
  26. // script:
  27. //
  28. // RefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv);
  29. // if (aRv.Failed()) {
  30. // return nullptr;
  31. // }
  32. //
  33. // 2. Create a PromiseWorkerProxy wrapping the Promise. If this fails, the
  34. // worker is shutting down and you should fail the original call. This is
  35. // only likely to happen in (Gecko-specific) worker onclose handlers.
  36. //
  37. // RefPtr<PromiseWorkerProxy> proxy =
  38. // PromiseWorkerProxy::Create(workerPrivate, promise);
  39. // if (!proxy) {
  40. // // You may also reject the Promise with an AbortError or similar.
  41. // return nullptr;
  42. // }
  43. //
  44. // 3. Dispatch a runnable to the main thread, with a reference to the proxy to
  45. // perform the main thread operation. PromiseWorkerProxy is thread-safe
  46. // refcounted.
  47. //
  48. // 4. Return the worker thread promise to the JS caller:
  49. //
  50. // return promise.forget();
  51. //
  52. // 5. In your main thread runnable Run(), obtain a Promise on
  53. // the main thread and call its AppendNativeHandler(PromiseNativeHandler*)
  54. // to bind the PromiseWorkerProxy created at #2.
  55. //
  56. // 4. Then the Promise results returned by ResolvedCallback/RejectedCallback
  57. // will be dispatched as a WorkerRunnable to the worker thread to
  58. // resolve/reject the Promise created at #1.
  59. //
  60. // PromiseWorkerProxy can also be used in situations where there is no main
  61. // thread Promise, or where special handling is required on the worker thread
  62. // for promise resolution. Create a PromiseWorkerProxy as in steps 1 to 3
  63. // above. When the main thread is ready to resolve the worker thread promise:
  64. //
  65. // 1. Acquire the mutex before attempting to access the worker private.
  66. //
  67. // AssertIsOnMainThread();
  68. // MutexAutoLock lock(proxy->Lock());
  69. // if (proxy->CleanedUp()) {
  70. // // Worker has already shut down, can't access worker private.
  71. // return;
  72. // }
  73. //
  74. // 2. Dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
  75. // worker.
  76. //
  77. // RefPtr<FinishTaskWorkerRunnable> runnable =
  78. // new FinishTaskWorkerRunnable(proxy->GetWorkerPrivate(), proxy, result);
  79. // if (!r->Dispatch()) {
  80. // // Worker is alive but not Running any more, so the Promise can't
  81. // // be resolved, give up. The proxy will get Release()d at some
  82. // // point.
  83. //
  84. // // Usually do nothing, but you may want to log the fact.
  85. // }
  86. //
  87. // 3. In the WorkerRunnable's WorkerRun() use WorkerPromise() to access the
  88. // Promise and resolve/reject it. Then call CleanUp().
  89. //
  90. // bool
  91. // WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
  92. // {
  93. // aWorkerPrivate->AssertIsOnWorkerThread();
  94. // RefPtr<Promise> promise = mProxy->WorkerPromise();
  95. // promise->MaybeResolve(mResult);
  96. // mProxy->CleanUp();
  97. // }
  98. //
  99. // Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
  100. // can happen if the main thread Promise is never fulfilled - it will
  101. // stay alive till the worker reaches a Canceling state, even if all external
  102. // references to it are dropped.
  103. class PromiseWorkerProxy : public PromiseNativeHandler
  104. , public StructuredCloneHolderBase
  105. {
  106. friend class PromiseWorkerProxyRunnable;
  107. NS_DECL_THREADSAFE_ISUPPORTS
  108. public:
  109. typedef JSObject* (*ReadCallbackOp)(JSContext* aCx,
  110. JSStructuredCloneReader* aReader,
  111. const PromiseWorkerProxy* aProxy,
  112. uint32_t aTag,
  113. uint32_t aData);
  114. typedef bool (*WriteCallbackOp)(JSContext* aCx,
  115. JSStructuredCloneWriter* aWorker,
  116. PromiseWorkerProxy* aProxy,
  117. JS::HandleObject aObj);
  118. struct PromiseWorkerProxyStructuredCloneCallbacks
  119. {
  120. ReadCallbackOp Read;
  121. WriteCallbackOp Write;
  122. };
  123. static already_AddRefed<PromiseWorkerProxy>
  124. Create(workers::WorkerPrivate* aWorkerPrivate,
  125. Promise* aWorkerPromise,
  126. const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);
  127. // Main thread callers must hold Lock() and check CleanUp() before calling this.
  128. // Worker thread callers, this will assert that the proxy has not been cleaned
  129. // up.
  130. workers::WorkerPrivate* GetWorkerPrivate() const;
  131. // This should only be used within WorkerRunnable::WorkerRun() running on the
  132. // worker thread! Do not call this after calling CleanUp().
  133. Promise* WorkerPromise() const;
  134. // Worker thread only. Calling this invalidates several assumptions, so be
  135. // sure this is the last thing you do.
  136. // 1. WorkerPrivate() will no longer return a valid worker.
  137. // 2. WorkerPromise() will crash!
  138. void CleanUp();
  139. Mutex& Lock()
  140. {
  141. return mCleanUpLock;
  142. }
  143. bool CleanedUp() const
  144. {
  145. mCleanUpLock.AssertCurrentThreadOwns();
  146. return mCleanedUp;
  147. }
  148. // StructuredCloneHolderBase
  149. JSObject* CustomReadHandler(JSContext* aCx,
  150. JSStructuredCloneReader* aReader,
  151. uint32_t aTag,
  152. uint32_t aIndex) override;
  153. bool CustomWriteHandler(JSContext* aCx,
  154. JSStructuredCloneWriter* aWriter,
  155. JS::Handle<JSObject*> aObj) override;
  156. protected:
  157. virtual void ResolvedCallback(JSContext* aCx,
  158. JS::Handle<JS::Value> aValue) override;
  159. virtual void RejectedCallback(JSContext* aCx,
  160. JS::Handle<JS::Value> aValue) override;
  161. private:
  162. PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
  163. Promise* aWorkerPromise,
  164. const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);
  165. virtual ~PromiseWorkerProxy();
  166. bool AddRefObject();
  167. // If not called from Create(), be sure to hold Lock().
  168. void CleanProperties();
  169. // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
  170. typedef void (Promise::*RunCallbackFunc)(JSContext*,
  171. JS::Handle<JS::Value>);
  172. void RunCallback(JSContext* aCx,
  173. JS::Handle<JS::Value> aValue,
  174. RunCallbackFunc aFunc);
  175. // Any thread with appropriate checks.
  176. workers::WorkerPrivate* mWorkerPrivate;
  177. // Worker thread only.
  178. RefPtr<Promise> mWorkerPromise;
  179. // Modified on the worker thread.
  180. // It is ok to *read* this without a lock on the worker.
  181. // Main thread must always acquire a lock.
  182. bool mCleanedUp; // To specify if the cleanUp() has been done.
  183. const PromiseWorkerProxyStructuredCloneCallbacks* mCallbacks;
  184. // Ensure the worker and the main thread won't race to access |mCleanedUp|.
  185. Mutex mCleanUpLock;
  186. UniquePtr<workers::WorkerHolder> mWorkerHolder;
  187. };
  188. } // namespace dom
  189. } // namespace mozilla
  190. #endif // mozilla_dom_PromiseWorkerProxy_h