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