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/. */
7 #ifndef mozilla_dom_PromiseWorkerProxy_h
8 #define mozilla_dom_PromiseWorkerProxy_h
11 #include "js/TypeDecls.h"
12 #include "mozilla/AlreadyAddRefed.h"
13 #include "mozilla/Mutex.h"
14 #include "mozilla/RefPtr.h"
15 #include "mozilla/dom/Promise.h"
16 #include "mozilla/dom/PromiseNativeHandler.h"
17 #include "mozilla/dom/StructuredCloneHolder.h"
18 #include "nsISupports.h"
20 struct JSStructuredCloneReader
;
21 struct JSStructuredCloneWriter
;
24 class CloneDataPolicy
;
30 class ThreadSafeWorkerRef
;
33 // A proxy to (eventually) mirror a resolved/rejected Promise's result from the
34 // main thread to a Promise on the worker thread.
38 // 1. Create a Promise on the worker thread and return it to the content
41 // RefPtr<Promise> promise =
42 // Promise::Create(workerPrivate->GlobalScope(), aRv);
43 // if (aRv.Failed()) {
47 // 2. Create a PromiseWorkerProxy wrapping the Promise. If this fails, the
48 // worker is shutting down and you should fail the original call. This is
49 // only likely to happen in (Gecko-specific) worker onclose handlers.
51 // RefPtr<PromiseWorkerProxy> proxy =
52 // PromiseWorkerProxy::Create(workerPrivate, promise);
54 // // You may also reject the Promise with an AbortError or similar.
58 // 3. Dispatch a runnable to the main thread, with a reference to the proxy to
59 // perform the main thread operation. PromiseWorkerProxy is thread-safe
62 // 4. Return the worker thread promise to the JS caller:
64 // return promise.forget();
66 // 5. In your main thread runnable Run(), obtain a Promise on
67 // the main thread and call its AppendNativeHandler(PromiseNativeHandler*)
68 // to bind the PromiseWorkerProxy created at #2.
70 // 4. Then the Promise results returned by ResolvedCallback/RejectedCallback
71 // will be dispatched as a WorkerRunnable to the worker thread to
72 // resolve/reject the Promise created at #1.
74 // PromiseWorkerProxy can also be used in situations where there is no main
75 // thread Promise, or where special handling is required on the worker thread
76 // for promise resolution. Create a PromiseWorkerProxy as in steps 1 to 3
77 // above. When the main thread is ready to resolve the worker thread promise:
79 // 1. Acquire the mutex before attempting to access the worker private.
81 // AssertIsOnMainThread();
82 // MutexAutoLock lock(proxy->Lock());
83 // if (proxy->CleanedUp()) {
84 // // Worker has already shut down, can't access worker private.
88 // 2. Dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
91 // RefPtr<FinishTaskWorkerRunnable> runnable =
92 // new FinishTaskWorkerRunnable(proxy->GetWorkerPrivate(), proxy,
94 // if (!r->Dispatch()) {
95 // // Worker is alive but not Running any more, so the Promise can't
96 // // be resolved, give up. The proxy will get Release()d at some
99 // // Usually do nothing, but you may want to log the fact.
102 // 3. In the WorkerRunnable's WorkerRun() use WorkerPromise() to access the
103 // Promise and resolve/reject it. Then call CleanUp().
106 // WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
108 // aWorkerPrivate->AssertIsOnWorkerThread();
109 // RefPtr<Promise> promise = mProxy->WorkerPromise();
110 // promise->MaybeResolve(mResult);
111 // mProxy->CleanUp();
114 // Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
115 // can happen if the main thread Promise is never fulfilled - it will
116 // stay alive till the worker reaches a Canceling state, even if all external
117 // references to it are dropped.
119 class PromiseWorkerProxy
: public PromiseNativeHandler
,
120 public StructuredCloneHolderBase
{
121 friend class PromiseWorkerProxyRunnable
;
123 NS_DECL_THREADSAFE_ISUPPORTS
126 typedef JSObject
* (*ReadCallbackOp
)(JSContext
* aCx
,
127 JSStructuredCloneReader
* aReader
,
128 const PromiseWorkerProxy
* aProxy
,
129 uint32_t aTag
, uint32_t aData
);
130 typedef bool (*WriteCallbackOp
)(JSContext
* aCx
,
131 JSStructuredCloneWriter
* aWorker
,
132 PromiseWorkerProxy
* aProxy
,
133 JS::HandleObject aObj
);
135 struct PromiseWorkerProxyStructuredCloneCallbacks
{
137 WriteCallbackOp Write
;
140 static already_AddRefed
<PromiseWorkerProxy
> Create(
141 WorkerPrivate
* aWorkerPrivate
, Promise
* aWorkerPromise
,
142 const PromiseWorkerProxyStructuredCloneCallbacks
* aCallbacks
= nullptr);
144 // Main thread callers must hold Lock() and check CleanUp() before calling
145 // this. Worker thread callers, this will assert that the proxy has not been
147 WorkerPrivate
* GetWorkerPrivate() const;
149 // This should only be used within WorkerRunnable::WorkerRun() running on the
150 // worker thread! Do not call this after calling CleanUp().
151 Promise
* WorkerPromise() const;
153 // Worker thread only. Calling this invalidates several assumptions, so be
154 // sure this is the last thing you do.
155 // 1. WorkerPrivate() will no longer return a valid worker.
156 // 2. WorkerPromise() will crash!
159 Mutex
& Lock() { return mCleanUpLock
; }
161 bool CleanedUp() const {
162 mCleanUpLock
.AssertCurrentThreadOwns();
166 // StructuredCloneHolderBase
168 JSObject
* CustomReadHandler(JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
169 const JS::CloneDataPolicy
& aCloneDataPolicy
,
170 uint32_t aTag
, uint32_t aIndex
) override
;
172 bool CustomWriteHandler(JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
,
173 JS::Handle
<JSObject
*> aObj
,
174 bool* aSameProcessScopeRequired
) override
;
177 virtual void ResolvedCallback(JSContext
* aCx
,
178 JS::Handle
<JS::Value
> aValue
) override
;
180 virtual void RejectedCallback(JSContext
* aCx
,
181 JS::Handle
<JS::Value
> aValue
) override
;
184 explicit PromiseWorkerProxy(
185 Promise
* aWorkerPromise
,
186 const PromiseWorkerProxyStructuredCloneCallbacks
* aCallbacks
= nullptr);
188 virtual ~PromiseWorkerProxy();
190 // If not called from Create(), be sure to hold Lock().
191 void CleanProperties();
193 // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
194 typedef void (Promise::*RunCallbackFunc
)(JSContext
*, JS::Handle
<JS::Value
>);
196 void RunCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
197 RunCallbackFunc aFunc
);
199 // Any thread with appropriate checks.
200 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
202 // Worker thread only.
203 RefPtr
<Promise
> mWorkerPromise
;
205 // Modified on the worker thread.
206 // It is ok to *read* this without a lock on the worker.
207 // Main thread must always acquire a lock.
208 bool mCleanedUp
; // To specify if the cleanUp() has been done.
210 const PromiseWorkerProxyStructuredCloneCallbacks
* mCallbacks
;
212 // Ensure the worker and the main thread won't race to access |mCleanedUp|.
216 } // namespace mozilla
218 #endif // mozilla_dom_PromiseWorkerProxy_h