123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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/workers/bindings/WorkerHolder.h"
- #include "nsProxyRelease.h"
- #include "WorkerRunnable.h"
- namespace mozilla {
- namespace dom {
- class Promise;
- namespace workers {
- class WorkerPrivate;
- } // namespace workers
- // 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(workers::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.
- workers::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(workers::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.
- workers::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<workers::WorkerHolder> mWorkerHolder;
- };
- } // namespace dom
- } // namespace mozilla
- #endif // mozilla_dom_PromiseWorkerProxy_h
|