зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1283924 - Implement AsyncTask hooks for worker thread JSContexts (r=bkelly)
MozReview-Commit-ID: CU4tiYaREik --HG-- extra : rebase_source : 35a1017de6844af78ec124ef84f355727f19cd80
This commit is contained in:
Родитель
ce850ee396
Коммит
cc5c2c3ffc
|
@ -65,10 +65,55 @@ function compileManySuccess() {
|
|||
);
|
||||
}
|
||||
|
||||
function compileInWorker() {
|
||||
var w = new Worker(`data:text/plain,
|
||||
onmessage = e => {
|
||||
WebAssembly.compile(e.data).then(m => {
|
||||
var i = new WebAssembly.Instance(m);
|
||||
if (i.exports.foo() !== 42)
|
||||
throw "bad i.exports.foo() result";
|
||||
postMessage("ok");
|
||||
close();
|
||||
}).catch(err => { throw err });
|
||||
}
|
||||
`);
|
||||
w.postMessage(fooModuleCode);
|
||||
w.onmessage = e => {
|
||||
ok(e.data === "ok", "worker test");
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
function terminateCompileInWorker() {
|
||||
var w = new Worker(`data:text/plain,
|
||||
var fooModuleCode;
|
||||
function spawnWork() {
|
||||
const N = 100;
|
||||
var arr = [];
|
||||
for (var i = 0; i < N; i++)
|
||||
arr.push(WebAssembly.compile(fooModuleCode));
|
||||
Promise.all(arr).then(spawnWork);
|
||||
}
|
||||
onmessage = e => {
|
||||
fooModuleCode = e.data;
|
||||
spawnWork();
|
||||
postMessage("ok");
|
||||
}
|
||||
`);
|
||||
w.postMessage(fooModuleCode);
|
||||
w.onmessage = e => {
|
||||
ok(e.data === "ok", "worker finished first step");
|
||||
w.terminate();
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
var tests = [ propertiesExist,
|
||||
compileFail,
|
||||
compileSuccess,
|
||||
compileManySuccess
|
||||
compileManySuccess,
|
||||
compileInWorker,
|
||||
terminateCompileInWorker
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
|
|
|
@ -713,6 +713,185 @@ AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
|
|||
aSize, aMemory, aHandle);
|
||||
}
|
||||
|
||||
class AsyncTaskWorkerHolder final : public WorkerHolder
|
||||
{
|
||||
bool Notify(Status aStatus) override
|
||||
{
|
||||
// The async task must complete in bounded time and there is not (currently)
|
||||
// a clean way to cancel it. Async tasks do not run arbitrary content.
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
WorkerPrivate* Worker() const
|
||||
{
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
};
|
||||
|
||||
template <class RunnableBase>
|
||||
class AsyncTaskBase : public RunnableBase
|
||||
{
|
||||
UniquePtr<AsyncTaskWorkerHolder> mHolder;
|
||||
|
||||
// Disable the usual pre/post-dispatch thread assertions since we are
|
||||
// dispatching from some random JS engine internal thread:
|
||||
|
||||
bool PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
||||
{ }
|
||||
|
||||
protected:
|
||||
explicit AsyncTaskBase(UniquePtr<AsyncTaskWorkerHolder> aHolder)
|
||||
: RunnableBase(aHolder->Worker(),
|
||||
WorkerRunnable::WorkerThreadUnchangedBusyCount)
|
||||
, mHolder(Move(aHolder))
|
||||
{
|
||||
MOZ_ASSERT(mHolder);
|
||||
}
|
||||
|
||||
~AsyncTaskBase()
|
||||
{
|
||||
MOZ_ASSERT(!mHolder);
|
||||
}
|
||||
|
||||
void DestroyHolder()
|
||||
{
|
||||
MOZ_ASSERT(mHolder);
|
||||
mHolder.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
UniquePtr<AsyncTaskWorkerHolder> StealHolder()
|
||||
{
|
||||
return Move(mHolder);
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncTaskRunnable final : public AsyncTaskBase<WorkerRunnable>
|
||||
{
|
||||
JS::AsyncTask* mTask;
|
||||
|
||||
~AsyncTaskRunnable()
|
||||
{
|
||||
MOZ_ASSERT(!mTask);
|
||||
}
|
||||
|
||||
void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
||||
{
|
||||
// For the benefit of the destructor assert.
|
||||
if (!aDispatchResult) {
|
||||
mTask = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AsyncTaskRunnable(UniquePtr<AsyncTaskWorkerHolder> aHolder,
|
||||
JS::AsyncTask* aTask)
|
||||
: AsyncTaskBase<WorkerRunnable>(Move(aHolder))
|
||||
, mTask(aTask)
|
||||
{
|
||||
MOZ_ASSERT(mTask);
|
||||
}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
|
||||
MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext());
|
||||
MOZ_ASSERT(mTask);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
mTask->finish(mWorkerPrivate->GetJSContext());
|
||||
mTask = nullptr; // mTask may delete itself
|
||||
|
||||
DestroyHolder();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult Cancel() override
|
||||
{
|
||||
MOZ_ASSERT(mTask);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
mTask->cancel(mWorkerPrivate->GetJSContext());
|
||||
mTask = nullptr; // mTask may delete itself
|
||||
|
||||
DestroyHolder();
|
||||
|
||||
return WorkerRunnable::Cancel();
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncTaskControlRunnable final
|
||||
: public AsyncTaskBase<WorkerControlRunnable>
|
||||
{
|
||||
public:
|
||||
explicit AsyncTaskControlRunnable(UniquePtr<AsyncTaskWorkerHolder> aHolder)
|
||||
: AsyncTaskBase<WorkerControlRunnable>(Move(aHolder))
|
||||
{ }
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
// See comment in FinishAsyncTaskCallback.
|
||||
DestroyHolder();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
StartAsyncTaskCallback(JSContext* aCx, JS::AsyncTask* aTask)
|
||||
{
|
||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
|
||||
auto holder = MakeUnique<AsyncTaskWorkerHolder>();
|
||||
if (!holder->HoldWorker(worker, Status::Closing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Matched by a UniquePtr in FinishAsyncTaskCallback which, by
|
||||
// interface contract, must be called in the future.
|
||||
aTask->user = holder.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
FinishAsyncTaskCallback(JS::AsyncTask* aTask)
|
||||
{
|
||||
// May execute either on the worker thread or a random JS-internal helper
|
||||
// thread.
|
||||
|
||||
// Match the release() in StartAsyncTaskCallback.
|
||||
UniquePtr<AsyncTaskWorkerHolder> holder(
|
||||
static_cast<AsyncTaskWorkerHolder*>(aTask->user));
|
||||
|
||||
RefPtr<AsyncTaskRunnable> r = new AsyncTaskRunnable(Move(holder), aTask);
|
||||
|
||||
// WorkerRunnable::Dispatch() can fail during worker shutdown. In that case,
|
||||
// report failure back to the JS engine but make sure to release the
|
||||
// WorkerHolder on the worker thread using a control runnable. Control
|
||||
// runables aren't suitable for calling AsyncTask::finish() since they are run
|
||||
// via the interrupt callback which breaks JS run-to-completion.
|
||||
if (!r->Dispatch()) {
|
||||
RefPtr<AsyncTaskControlRunnable> cr =
|
||||
new AsyncTaskControlRunnable(r->StealHolder());
|
||||
|
||||
MOZ_ALWAYS_TRUE(cr->Dispatch());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class WorkerJSRuntime;
|
||||
|
||||
class WorkerThreadContextPrivate : private PerThreadAtomCache
|
||||
|
@ -808,6 +987,8 @@ InitJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSContext* aWorkerCx)
|
|||
};
|
||||
JS::SetAsmJSCacheOps(aWorkerCx, &asmJSCacheOps);
|
||||
|
||||
JS::SetAsyncTaskCallbacks(aWorkerCx, StartAsyncTaskCallback, FinishAsyncTaskCallback);
|
||||
|
||||
if (!JS::InitSelfHostedCode(aWorkerCx)) {
|
||||
NS_WARNING("Could not init self-hosted code!");
|
||||
return false;
|
||||
|
|
Загрузка…
Ссылка в новой задаче