mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 949325 - C++ wrapper to support DataStore API on the worker (part 2-1, provide a proxy to resolve/reject Promise on workers). r=baku,nsm,khuey
This commit is contained in:
parent
70c99769f4
commit
9a6ee307a3
@ -13,6 +13,7 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "PromiseCallback.h"
|
||||
#include "PromiseNativeHandler.h"
|
||||
#include "PromiseWorkerProxy.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
@ -694,13 +695,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
ResolvedCallback(JS::Handle<JS::Value> aValue)
|
||||
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
mCountdownHolder->SetValue(mIndex, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
RejectedCallback(JS::Handle<JS::Value> aValue)
|
||||
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
// Should never be attached to Promise as a reject handler.
|
||||
MOZ_ASSERT(false, "AllResolveHandler should never be attached to a Promise's reject handler!");
|
||||
@ -1125,5 +1126,200 @@ PromiseReportRejectFeature::Notify(JSContext* aCx, workers::Status aStatus)
|
||||
return true;
|
||||
}
|
||||
|
||||
// A WorkerRunnable to resolve/reject the Promise on the worker thread.
|
||||
|
||||
class PromiseWorkerProxyRunnable : public workers::WorkerRunnable
|
||||
{
|
||||
public:
|
||||
PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
|
||||
JSStructuredCloneCallbacks* aCallbacks,
|
||||
JSAutoStructuredCloneBuffer&& aBuffer,
|
||||
PromiseWorkerProxy::RunCallbackFunc aFunc)
|
||||
: WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(),
|
||||
WorkerThreadUnchangedBusyCount)
|
||||
, mPromiseWorkerProxy(aPromiseWorkerProxy)
|
||||
, mCallbacks(aCallbacks)
|
||||
, mBuffer(Move(aBuffer))
|
||||
, mFunc(aFunc)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mPromiseWorkerProxy);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
|
||||
|
||||
MOZ_ASSERT(mPromiseWorkerProxy);
|
||||
nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->GetWorkerPromise();
|
||||
MOZ_ASSERT(workerPromise);
|
||||
|
||||
// Here we convert the buffer to a JS::Value.
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!mBuffer.read(aCx, &value, mCallbacks, mPromiseWorkerProxy)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO Bug 975246 - nsRefPtr should support operator |nsRefPtr->*funcType|.
|
||||
(workerPromise.get()->*mFunc)(aCx,
|
||||
value,
|
||||
Promise::PromiseTaskSync::SyncTask);
|
||||
|
||||
// Release the Promise because it has been resolved/rejected for sure.
|
||||
mPromiseWorkerProxy->CleanUp(aCx);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
~PromiseWorkerProxyRunnable() {}
|
||||
|
||||
private:
|
||||
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
|
||||
JSStructuredCloneCallbacks* mCallbacks;
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
|
||||
// Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
|
||||
PromiseWorkerProxy::RunCallbackFunc mFunc;
|
||||
};
|
||||
|
||||
PromiseWorkerProxy::PromiseWorkerProxy(WorkerPrivate* aWorkerPrivate,
|
||||
Promise* aWorkerPromise,
|
||||
JSStructuredCloneCallbacks* aCallbacks)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
, mWorkerPromise(aWorkerPromise)
|
||||
, mCleanedUp(false)
|
||||
, mCallbacks(aCallbacks)
|
||||
, mCleanUpLock("cleanUpLock")
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPromise);
|
||||
|
||||
// We do this to make sure the worker thread won't shut down before the
|
||||
// promise is resolved/rejected on the worker thread.
|
||||
if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
|
||||
MOZ_ASSERT(false, "cannot add the worker feature!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PromiseWorkerProxy::~PromiseWorkerProxy()
|
||||
{
|
||||
MOZ_ASSERT(mCleanedUp);
|
||||
MOZ_ASSERT(!mWorkerPromise);
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
PromiseWorkerProxy::GetWorkerPrivate() const
|
||||
{
|
||||
// It's ok to race on |mCleanedUp|, because it will never cause us to fire
|
||||
// the assertion when we should not.
|
||||
MOZ_ASSERT(!mCleanedUp);
|
||||
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
Promise*
|
||||
PromiseWorkerProxy::GetWorkerPromise() const
|
||||
{
|
||||
return mWorkerPromise;
|
||||
}
|
||||
|
||||
void
|
||||
PromiseWorkerProxy::StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsMainThreadPtrHandle<nsISupports> supports =
|
||||
new nsMainThreadPtrHolder<nsISupports>(aSupports);
|
||||
mSupportsArray.AppendElement(supports);
|
||||
}
|
||||
|
||||
void
|
||||
PromiseWorkerProxy::RunCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
RunCallbackFunc aFunc)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MutexAutoLock lock(mCleanUpLock);
|
||||
// If the worker thread's been cancelled we don't need to resolve the Promise.
|
||||
if (mCleanedUp) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The |aValue| is written into the buffer. Note that we also pass |this|
|
||||
// into the structured-clone write in order to set its |mSupportsArray| to
|
||||
// keep objects alive until the structured-clone read/write is done.
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(aCx, aValue, mCallbacks, this)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
MOZ_ASSERT(false, "cannot write the JSAutoStructuredCloneBuffer!");
|
||||
}
|
||||
|
||||
nsRefPtr<PromiseWorkerProxyRunnable> runnable =
|
||||
new PromiseWorkerProxyRunnable(this,
|
||||
mCallbacks,
|
||||
Move(buffer),
|
||||
aFunc);
|
||||
|
||||
runnable->Dispatch(aCx);
|
||||
}
|
||||
|
||||
void
|
||||
PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
RunCallback(aCx, aValue, &Promise::ResolveInternal);
|
||||
}
|
||||
|
||||
void
|
||||
PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
RunCallback(aCx, aValue, &Promise::RejectInternal);
|
||||
}
|
||||
|
||||
bool
|
||||
PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
|
||||
|
||||
if (aStatus >= Canceling) {
|
||||
CleanUp(aCx);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PromiseWorkerProxy::CleanUp(JSContext* aCx)
|
||||
{
|
||||
MutexAutoLock lock(mCleanUpLock);
|
||||
|
||||
// |mWorkerPrivate| might not be safe to use anymore if we have already
|
||||
// cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
|
||||
if (mCleanedUp) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
|
||||
|
||||
// Release the Promise and remove the PromiseWorkerProxy from the features of
|
||||
// the worker thread since the Promise has been resolved/rejected or the
|
||||
// worker thread has been cancelled.
|
||||
mWorkerPromise = nullptr;
|
||||
mWorkerPrivate->RemoveFeature(aCx, this);
|
||||
mCleanedUp = true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -55,6 +55,8 @@ class Promise MOZ_FINAL : public nsISupports,
|
||||
friend class PromiseResolverTask;
|
||||
friend class PromiseTask;
|
||||
friend class PromiseReportRejectFeature;
|
||||
friend class PromiseWorkerProxy;
|
||||
friend class PromiseWorkerProxyRunnable;
|
||||
friend class RejectPromiseCallback;
|
||||
friend class ResolvePromiseCallback;
|
||||
friend class WorkerPromiseResolverTask;
|
||||
|
@ -338,12 +338,12 @@ NativePromiseCallback::Call(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
if (mState == Promise::Resolved) {
|
||||
mHandler->ResolvedCallback(aValue);
|
||||
mHandler->ResolvedCallback(aCx, aValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState == Promise::Rejected) {
|
||||
mHandler->RejectedCallback(aValue);
|
||||
mHandler->RejectedCallback(aCx, aValue);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,10 @@ public:
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
ResolvedCallback(JS::Handle<JS::Value> aValue) = 0;
|
||||
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) = 0;
|
||||
|
||||
virtual void
|
||||
RejectedCallback(JS::Handle<JS::Value> aValue) = 0;
|
||||
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) = 0;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
106
dom/promise/PromiseWorkerProxy.h
Normal file
106
dom/promise/PromiseWorkerProxy.h
Normal file
@ -0,0 +1,106 @@
|
||||
/* 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
|
||||
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/workers/bindings/WorkerFeature.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
}
|
||||
|
||||
// A proxy to catch the resolved/rejected Promise's result from the main thread
|
||||
// and resolve/reject that on the worker thread eventually.
|
||||
//
|
||||
// How to use:
|
||||
//
|
||||
// 1. Create a Promise on the worker thread and return it to the content
|
||||
// script:
|
||||
//
|
||||
// nsRefPtr<Promise> promise = new Promise(workerPrivate->GlobalScope());
|
||||
// // Pass |promise| around to the WorkerMainThreadRunnable
|
||||
// return promise.forget();
|
||||
//
|
||||
// 2. In your WorkerMainThreadRunnable's ctor, create a PromiseWorkerProxy
|
||||
// which holds a nsRefPtr<Promise> to the Promise created at #1.
|
||||
//
|
||||
// 3. In your WorkerMainThreadRunnable::MainThreadRun(), 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.
|
||||
|
||||
class PromiseWorkerProxy : public PromiseNativeHandler,
|
||||
public workers::WorkerFeature
|
||||
{
|
||||
friend class PromiseWorkerProxyRunnable;
|
||||
|
||||
// This overrides the non-threadsafe refcounting in PromiseNativeHandler.
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseWorkerProxy)
|
||||
|
||||
public:
|
||||
PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
|
||||
Promise* aWorkerPromise,
|
||||
JSStructuredCloneCallbacks* aCallbacks = nullptr);
|
||||
|
||||
workers::WorkerPrivate* GetWorkerPrivate() const;
|
||||
|
||||
Promise* GetWorkerPromise() const;
|
||||
|
||||
void StoreISupports(nsISupports* aSupports);
|
||||
|
||||
void CleanUp(JSContext* aCx);
|
||||
|
||||
protected:
|
||||
virtual void ResolvedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
|
||||
|
||||
virtual void RejectedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual ~PromiseWorkerProxy();
|
||||
|
||||
// Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
|
||||
typedef void (Promise::*RunCallbackFunc)(JSContext*,
|
||||
JS::Handle<JS::Value>,
|
||||
Promise::PromiseTaskSync);
|
||||
|
||||
void RunCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
RunCallbackFunc aFunc);
|
||||
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// This lives on the worker thread.
|
||||
nsRefPtr<Promise> mWorkerPromise;
|
||||
|
||||
bool mCleanedUp; // To specify if the cleanUp() has been done.
|
||||
|
||||
JSStructuredCloneCallbacks* mCallbacks;
|
||||
|
||||
// Aimed to keep objects alive when doing the structured-clone read/write,
|
||||
// which can be added by calling StoreISupports() on the main thread.
|
||||
nsTArray<nsMainThreadPtrHandle<nsISupports>> mSupportsArray;
|
||||
|
||||
// Ensure the worker and the main thread won't race to access |mCleanedUp|.
|
||||
Mutex mCleanUpLock;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PromiseWorkerProxy_h
|
@ -8,7 +8,8 @@ TEST_DIRS += ['tests']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Promise.h',
|
||||
'PromiseNativeHandler.h'
|
||||
'PromiseNativeHandler.h',
|
||||
'PromiseWorkerProxy.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
|
Loading…
Reference in New Issue
Block a user