mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 16:22:00 +00:00
d57c9498f6
This flag is not supported by most event targets and can have unexpected side effects (namely spinning a nested event loop). All consumers have been replaced with a new function which is more explicit about this side effect. Differential Revision: https://phabricator.services.mozilla.com/D173985
137 lines
4.4 KiB
C++
137 lines
4.4 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 "ThreadEventTarget.h"
|
|
#include "mozilla/ThreadEventQueue.h"
|
|
|
|
#include "LeakRefPtr.h"
|
|
#include "mozilla/DelayedRunnable.h"
|
|
#include "mozilla/SpinEventLoopUntil.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsITimer.h"
|
|
#include "nsThreadManager.h"
|
|
#include "nsThreadSyncDispatch.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "ThreadDelay.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
#ifdef DEBUG
|
|
// This flag will be set right after XPCOMShutdownThreads finished but before
|
|
// we continue with other processing. It is exclusively meant to prime the
|
|
// assertion of ThreadEventTarget::Dispatch as early as possible.
|
|
// Please use AppShutdown::IsInOrBeyond(ShutdownPhase::???)
|
|
// elsewhere to check for shutdown phases.
|
|
static mozilla::Atomic<bool, mozilla::SequentiallyConsistent>
|
|
gXPCOMThreadsShutDownNotified(false);
|
|
#endif
|
|
|
|
ThreadEventTarget::ThreadEventTarget(ThreadTargetSink* aSink,
|
|
bool aIsMainThread, bool aBlockDispatch)
|
|
: mSink(aSink),
|
|
#ifdef DEBUG
|
|
mIsMainThread(aIsMainThread),
|
|
#endif
|
|
mBlockDispatch(aBlockDispatch) {
|
|
mThread = PR_GetCurrentThread();
|
|
}
|
|
|
|
ThreadEventTarget::~ThreadEventTarget() = default;
|
|
|
|
void ThreadEventTarget::SetCurrentThread(PRThread* aThread) {
|
|
mThread = aThread;
|
|
}
|
|
|
|
void ThreadEventTarget::ClearCurrentThread() { mThread = nullptr; }
|
|
|
|
NS_IMPL_ISUPPORTS(ThreadEventTarget, nsIEventTarget, nsISerialEventTarget)
|
|
|
|
NS_IMETHODIMP
|
|
ThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) {
|
|
return Dispatch(do_AddRef(aRunnable), aFlags);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// static
|
|
void ThreadEventTarget::XPCOMShutdownThreadsNotificationFinished() {
|
|
gXPCOMThreadsShutDownNotified = true;
|
|
}
|
|
#endif
|
|
|
|
NS_IMETHODIMP
|
|
ThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent,
|
|
uint32_t aFlags) {
|
|
// We want to leak the reference when we fail to dispatch it, so that
|
|
// we won't release the event in a wrong thread.
|
|
LeakRefPtr<nsIRunnable> event(std::move(aEvent));
|
|
if (NS_WARN_IF(!event)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
NS_ASSERTION(!gXPCOMThreadsShutDownNotified || mIsMainThread ||
|
|
PR_GetCurrentThread() == mThread,
|
|
"Dispatch to non-main thread after xpcom-shutdown-threads");
|
|
|
|
if (mBlockDispatch && !(aFlags & NS_DISPATCH_IGNORE_BLOCK_DISPATCH)) {
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
false,
|
|
"Attempt to dispatch to thread which does not usually process "
|
|
"dispatched runnables until shutdown");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
LogRunnable::LogDispatch(event.get());
|
|
|
|
NS_ASSERTION((aFlags & (NS_DISPATCH_AT_END |
|
|
NS_DISPATCH_IGNORE_BLOCK_DISPATCH)) == aFlags,
|
|
"unexpected dispatch flags");
|
|
if (!mSink->PutEvent(event.take(), EventQueuePriority::Normal)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
// Delay to encourage the receiving task to run before we do work.
|
|
DelayForChaosMode(ChaosFeature::TaskDispatching, 1000);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ThreadEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
|
|
uint32_t aDelayMs) {
|
|
nsCOMPtr<nsIRunnable> event = aEvent;
|
|
NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);
|
|
|
|
RefPtr<DelayedRunnable> r =
|
|
new DelayedRunnable(do_AddRef(this), event.forget(), aDelayMs);
|
|
nsresult rv = r->Init();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ThreadEventTarget::RegisterShutdownTask(nsITargetShutdownTask* aTask) {
|
|
return mSink->RegisterShutdownTask(aTask);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ThreadEventTarget::UnregisterShutdownTask(nsITargetShutdownTask* aTask) {
|
|
return mSink->UnregisterShutdownTask(aTask);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
|
|
*aIsOnCurrentThread = IsOnCurrentThread();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(bool)
|
|
ThreadEventTarget::IsOnCurrentThreadInfallible() {
|
|
// This method is only going to be called if `mThread` is null, which
|
|
// only happens when the thread has exited the event loop. Therefore, when
|
|
// we are called, we can never be on this thread.
|
|
return false;
|
|
}
|