mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-14 02:31:59 +00:00
3e1b6b5341
When the RegisterShutdownTask method was implemented for WorkerEventTarget in bug 1809044, it didn't follow the requirements for error return values and edge-case handling described in the nsIEventTarget.idl file's documentation. This patch updates the method to behave more consistently with other shutdown task methods such as those on nsThread. Specifically, the method would not handle `aTask` being nullptr, asserting instead, and would return `NS_ERROR_FAILURE` rather than `NS_ERROR_UNEXPECTED` after the thread had shut down. Ideally, for maximum consistency with nsThread, the worker thread would continue to accept new event dispatches during shutdown tasks to ensure code which uses `RegisterShutdownTasks` has a chance to cancel any future dispatches to the target thread before the thread dies (e.g. to prevent leaks), but the API does allow the behaviour of not allowing new dispatches earlier as it's not possible to implement that behaviour for all event targets. Differential Revision: https://phabricator.services.mozilla.com/D170537
168 lines
4.6 KiB
C++
168 lines
4.6 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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/. */
|
|
|
|
#include "WorkerEventTarget.h"
|
|
#include "WorkerPrivate.h"
|
|
#include "WorkerRunnable.h"
|
|
|
|
#include "mozilla/dom/ReferrerInfo.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
namespace {
|
|
|
|
class WrappedControlRunnable final : public WorkerControlRunnable {
|
|
nsCOMPtr<nsIRunnable> mInner;
|
|
|
|
~WrappedControlRunnable() = default;
|
|
|
|
public:
|
|
WrappedControlRunnable(WorkerPrivate* aWorkerPrivate,
|
|
nsCOMPtr<nsIRunnable>&& aInner)
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
|
mInner(std::move(aInner)) {}
|
|
|
|
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
|
|
// Silence bad assertions, this can be dispatched from any thread.
|
|
return true;
|
|
}
|
|
|
|
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
|
|
bool aDispatchResult) override {
|
|
// Silence bad assertions, this can be dispatched from any thread.
|
|
}
|
|
|
|
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
|
|
mInner->Run();
|
|
return true;
|
|
}
|
|
|
|
nsresult Cancel() override {
|
|
nsCOMPtr<nsICancelableRunnable> cr = do_QueryInterface(mInner);
|
|
|
|
// If the inner runnable is not cancellable, then just do the normal
|
|
// WorkerControlRunnable thing. This will end up calling Run().
|
|
if (!cr) {
|
|
WorkerControlRunnable::Cancel();
|
|
return NS_OK;
|
|
}
|
|
|
|
// Otherwise call the inner runnable's Cancel() and treat this like
|
|
// a WorkerRunnable cancel. We can't call WorkerControlRunnable::Cancel()
|
|
// in this case since that would result in both Run() and the inner
|
|
// Cancel() being called.
|
|
Unused << cr->Cancel();
|
|
return WorkerRunnable::Cancel();
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
NS_IMPL_ISUPPORTS(WorkerEventTarget, nsIEventTarget, nsISerialEventTarget)
|
|
|
|
WorkerEventTarget::WorkerEventTarget(WorkerPrivate* aWorkerPrivate,
|
|
Behavior aBehavior)
|
|
: mMutex("WorkerEventTarget"),
|
|
mWorkerPrivate(aWorkerPrivate),
|
|
mBehavior(aBehavior) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate);
|
|
}
|
|
|
|
void WorkerEventTarget::ForgetWorkerPrivate(WorkerPrivate* aWorkerPrivate) {
|
|
MutexAutoLock lock(mMutex);
|
|
MOZ_DIAGNOSTIC_ASSERT(!mWorkerPrivate || mWorkerPrivate == aWorkerPrivate);
|
|
mWorkerPrivate = nullptr;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WorkerEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) {
|
|
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
|
return Dispatch(runnable.forget(), aFlags);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
|
|
uint32_t aFlags) {
|
|
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (!mWorkerPrivate) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (mBehavior == Behavior::Hybrid) {
|
|
RefPtr<WorkerRunnable> r =
|
|
mWorkerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget());
|
|
if (r->Dispatch()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
runnable = std::move(r);
|
|
}
|
|
|
|
RefPtr<WorkerControlRunnable> r =
|
|
new WrappedControlRunnable(mWorkerPrivate, std::move(runnable));
|
|
if (!r->Dispatch()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WorkerEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WorkerEventTarget::RegisterShutdownTask(nsITargetShutdownTask* aTask) {
|
|
NS_ENSURE_ARG(aTask);
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// If mWorkerPrivate is gone, the event target is already late during
|
|
// shutdown, return NS_ERROR_UNEXPECTED as documented in `nsIEventTarget.idl`.
|
|
if (!mWorkerPrivate) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
return mWorkerPrivate->RegisterShutdownTask(aTask);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WorkerEventTarget::UnregisterShutdownTask(nsITargetShutdownTask* aTask) {
|
|
NS_ENSURE_ARG(aTask);
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (!mWorkerPrivate) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
return mWorkerPrivate->UnregisterShutdownTask(aTask);
|
|
}
|
|
|
|
NS_IMETHODIMP_(bool)
|
|
WorkerEventTarget::IsOnCurrentThreadInfallible() {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (!mWorkerPrivate) {
|
|
return false;
|
|
}
|
|
|
|
return mWorkerPrivate->IsOnCurrentThread();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WorkerEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
|
|
MOZ_ASSERT(aIsOnCurrentThread);
|
|
*aIsOnCurrentThread = IsOnCurrentThreadInfallible();
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace mozilla::dom
|