diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /xpcom/threads/ThreadEventTarget.cpp | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpcom/threads/ThreadEventTarget.cpp')
-rw-r--r-- | xpcom/threads/ThreadEventTarget.cpp | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/xpcom/threads/ThreadEventTarget.cpp b/xpcom/threads/ThreadEventTarget.cpp new file mode 100644 index 0000000000..4ae989a3cc --- /dev/null +++ b/xpcom/threads/ThreadEventTarget.cpp @@ -0,0 +1,166 @@ +/* -*- 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()); + + if (aFlags & DISPATCH_SYNC) { + nsCOMPtr<nsIEventTarget> current = GetCurrentEventTarget(); + if (NS_WARN_IF(!current)) { + return NS_ERROR_NOT_AVAILABLE; + } + + // XXX we should be able to do something better here... we should + // be able to monitor the slot occupied by this event and use + // that to tell us when the event has been processed. + + RefPtr<nsThreadSyncDispatch> wrapper = + new nsThreadSyncDispatch(current.forget(), event.take()); + bool success = mSink->PutEvent(do_AddRef(wrapper), + EventQueuePriority::Normal); // hold a ref + if (!success) { + // PutEvent leaked the wrapper runnable object on failure, so we + // explicitly release this object once for that. Note that this + // object will be released again soon because it exits the scope. + wrapper.get()->Release(); + return NS_ERROR_UNEXPECTED; + } + + // Allows waiting; ensure no locks are held that would deadlock us! + SpinEventLoopUntil( + "ThreadEventTarget::Dispatch"_ns, + [&, wrapper]() -> bool { return !wrapper->IsPending(); }); + + return NS_OK; + } + + 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; +} |