no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / promise / PromiseWorkerProxy.h
blob026fdd091fbf0bf333abedfb6f9ba61c920731c2
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
10 #include <cstdint>
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;
23 namespace JS {
24 class CloneDataPolicy;
25 } // namespace JS
27 namespace mozilla::dom {
29 class ThreadSafeWorkerRef;
30 class WorkerPrivate;
32 // A proxy to (eventually) mirror a resolved/rejected Promise's result from the
33 // main thread to a Promise on the worker thread.
35 // How to use:
37 // 1. Create a Promise on the worker thread and return it to the content
38 // script:
40 // RefPtr<Promise> promise =
41 // Promise::Create(workerPrivate->GlobalScope(), aRv);
42 // if (aRv.Failed()) {
43 // return nullptr;
44 // }
46 // 2. Create a PromiseWorkerProxy wrapping the Promise. If this fails, the
47 // worker is shutting down and you should fail the original call. This is
48 // only likely to happen in (Gecko-specific) worker onclose handlers.
50 // RefPtr<PromiseWorkerProxy> proxy =
51 // PromiseWorkerProxy::Create(workerPrivate, promise);
52 // if (!proxy) {
53 // // You may also reject the Promise with an AbortError or similar.
54 // return nullptr;
55 // }
57 // 3. Dispatch a runnable to the main thread, with a reference to the proxy to
58 // perform the main thread operation. PromiseWorkerProxy is thread-safe
59 // refcounted.
61 // 4. Return the worker thread promise to the JS caller:
63 // return promise.forget();
65 // 5. In your main thread runnable Run(), obtain a Promise on
66 // the main thread and call its AppendNativeHandler(PromiseNativeHandler*)
67 // to bind the PromiseWorkerProxy created at #2.
69 // 4. Then the Promise results returned by ResolvedCallback/RejectedCallback
70 // will be dispatched as a WorkerRunnable to the worker thread to
71 // resolve/reject the Promise created at #1.
73 // PromiseWorkerProxy can also be used in situations where there is no main
74 // thread Promise, or where special handling is required on the worker thread
75 // for promise resolution. Create a PromiseWorkerProxy as in steps 1 to 3
76 // above. When the main thread is ready to resolve the worker thread promise:
78 // 1. Acquire the mutex before attempting to access the worker private.
80 // AssertIsOnMainThread();
81 // MutexAutoLock lock(proxy->Lock());
82 // if (proxy->CleanedUp()) {
83 // // Worker has already shut down, can't access worker private.
84 // return;
85 // }
87 // 2. Dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
88 // worker.
90 // RefPtr<FinishTaskWorkerRunnable> runnable =
91 // new FinishTaskWorkerRunnable(proxy->GetWorkerPrivate(), proxy,
92 // result);
93 // if (!r->Dispatch()) {
94 // // Worker is alive but not Running any more, so the Promise can't
95 // // be resolved, give up. The proxy will get Release()d at some
96 // // point.
98 // // Usually do nothing, but you may want to log the fact.
99 // }
101 // 3. In the WorkerRunnable's WorkerRun() use GetWorkerPromise() to access the
102 // Promise and resolve/reject it. Then call CleanUp().
104 // bool
105 // WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
106 // {
107 // aWorkerPrivate->AssertIsOnWorkerThread();
108 // RefPtr<Promise> promise = mProxy->GetWorkerPromise();
109 // promise->MaybeResolve(mResult);
110 // mProxy->CleanUp();
111 // }
113 // Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
114 // can happen if the main thread Promise is never fulfilled - it will
115 // stay alive till the worker reaches a Canceling state, even if all external
116 // references to it are dropped.
118 class PromiseWorkerProxy : public PromiseNativeHandler,
119 public StructuredCloneHolderBase,
120 public SingleWriterLockOwner {
121 friend class PromiseWorkerProxyRunnable;
123 NS_DECL_THREADSAFE_ISUPPORTS
125 public:
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::Handle<JSObject*> aObj);
135 bool OnWritingThread() const override;
137 struct PromiseWorkerProxyStructuredCloneCallbacks {
138 ReadCallbackOp Read;
139 WriteCallbackOp Write;
142 static already_AddRefed<PromiseWorkerProxy> Create(
143 WorkerPrivate* aWorkerPrivate, Promise* aWorkerPromise,
144 const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);
146 // Main thread callers must hold Lock() and check CleanUp() before calling
147 // this. Worker thread callers, this will assert that the proxy has not been
148 // cleaned up.
149 WorkerPrivate* GetWorkerPrivate() const MOZ_NO_THREAD_SAFETY_ANALYSIS;
151 // This should only be used within WorkerRunnable::WorkerRun() running on the
152 // worker thread! If this method is called after CleanUp(), return nullptr.
153 Promise* GetWorkerPromise() const;
155 // Worker thread only. Calling this invalidates several assumptions, so be
156 // sure this is the last thing you do.
157 // 1. WorkerPrivate() will no longer return a valid worker.
158 // 2. GetWorkerPromise() will return null!
159 void CleanUp();
161 Mutex& Lock() MOZ_RETURN_CAPABILITY(mCleanUpLock) { return mCleanUpLock; }
163 bool CleanedUp() const MOZ_REQUIRES(mCleanUpLock) {
164 mCleanUpLock.AssertCurrentThreadOwns();
165 return mCleanedUp;
168 // StructuredCloneHolderBase
170 JSObject* CustomReadHandler(JSContext* aCx, JSStructuredCloneReader* aReader,
171 const JS::CloneDataPolicy& aCloneDataPolicy,
172 uint32_t aTag, uint32_t aIndex) override;
174 bool CustomWriteHandler(JSContext* aCx, JSStructuredCloneWriter* aWriter,
175 JS::Handle<JSObject*> aObj,
176 bool* aSameProcessScopeRequired) override;
178 protected:
179 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
180 ErrorResult& aRv) override;
182 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
183 ErrorResult& aRv) override;
185 private:
186 explicit PromiseWorkerProxy(
187 Promise* aWorkerPromise,
188 const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);
190 virtual ~PromiseWorkerProxy();
192 // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
193 typedef void (Promise::*RunCallbackFunc)(JSContext*, JS::Handle<JS::Value>);
195 void RunCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
196 RunCallbackFunc aFunc);
198 // Any thread with appropriate checks.
199 RefPtr<ThreadSafeWorkerRef> mWorkerRef;
201 // Worker thread only.
202 RefPtr<Promise> mWorkerPromise;
204 // Modified on the worker thread.
205 // It is ok to *read* this without a lock on the worker.
206 // Main thread must always acquire a lock.
207 bool mCleanedUp MOZ_GUARDED_BY(
208 mCleanUpLock); // 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|.
213 // Should be a MutexSingleWriter, but that causes a lot of issues when you
214 // expose the lock via Lock().
215 Mutex mCleanUpLock;
217 } // namespace mozilla::dom
219 #endif // mozilla_dom_PromiseWorkerProxy_h