Bug 1755481: correct documentation of `nsIClipboard::getData`. r=mccr8
[gecko.git] / xpcom / threads / ThreadEventTarget.cpp
blob3e36b000289a3e96409b53be2b60a457a36d9e2f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ThreadEventTarget.h"
8 #include "mozilla/ThreadEventQueue.h"
10 #include "LeakRefPtr.h"
11 #include "mozilla/DelayedRunnable.h"
12 #include "mozilla/SpinEventLoopUntil.h"
13 #include "mozilla/TimeStamp.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsITimer.h"
16 #include "nsThreadManager.h"
17 #include "nsThreadSyncDispatch.h"
18 #include "nsThreadUtils.h"
19 #include "nsXPCOMPrivate.h" // for gXPCOMThreadsShutDown
20 #include "ThreadDelay.h"
22 using namespace mozilla;
24 ThreadEventTarget::ThreadEventTarget(ThreadTargetSink* aSink,
25 bool aIsMainThread)
26 : mSink(aSink), mIsMainThread(aIsMainThread) {
27 mThread = PR_GetCurrentThread();
30 ThreadEventTarget::~ThreadEventTarget() {
31 MOZ_ASSERT(mScheduledDelayedRunnables.IsEmpty());
34 void ThreadEventTarget::SetCurrentThread(PRThread* aThread) {
35 mThread = aThread;
38 void ThreadEventTarget::ClearCurrentThread() { mThread = nullptr; }
40 NS_IMPL_ISUPPORTS(ThreadEventTarget, nsIEventTarget, nsISerialEventTarget,
41 nsIDelayedRunnableObserver)
43 NS_IMETHODIMP
44 ThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) {
45 return Dispatch(do_AddRef(aRunnable), aFlags);
48 NS_IMETHODIMP
49 ThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent,
50 uint32_t aFlags) {
51 // We want to leak the reference when we fail to dispatch it, so that
52 // we won't release the event in a wrong thread.
53 LeakRefPtr<nsIRunnable> event(std::move(aEvent));
54 if (NS_WARN_IF(!event)) {
55 return NS_ERROR_INVALID_ARG;
58 if (gXPCOMThreadsShutDown && !mIsMainThread) {
59 NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
60 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
63 LogRunnable::LogDispatch(event.get());
65 if (aFlags & DISPATCH_SYNC) {
66 nsCOMPtr<nsIEventTarget> current = GetCurrentEventTarget();
67 if (NS_WARN_IF(!current)) {
68 return NS_ERROR_NOT_AVAILABLE;
71 // XXX we should be able to do something better here... we should
72 // be able to monitor the slot occupied by this event and use
73 // that to tell us when the event has been processed.
75 RefPtr<nsThreadSyncDispatch> wrapper =
76 new nsThreadSyncDispatch(current.forget(), event.take());
77 bool success = mSink->PutEvent(do_AddRef(wrapper),
78 EventQueuePriority::Normal); // hold a ref
79 if (!success) {
80 // PutEvent leaked the wrapper runnable object on failure, so we
81 // explicitly release this object once for that. Note that this
82 // object will be released again soon because it exits the scope.
83 wrapper.get()->Release();
84 return NS_ERROR_UNEXPECTED;
87 // Allows waiting; ensure no locks are held that would deadlock us!
88 SpinEventLoopUntil(
89 "ThreadEventTarget::Dispatch"_ns,
90 [&, wrapper]() -> bool { return !wrapper->IsPending(); });
92 return NS_OK;
95 NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL || aFlags == NS_DISPATCH_AT_END,
96 "unexpected dispatch flags");
97 if (!mSink->PutEvent(event.take(), EventQueuePriority::Normal)) {
98 return NS_ERROR_UNEXPECTED;
100 // Delay to encourage the receiving task to run before we do work.
101 DelayForChaosMode(ChaosFeature::TaskDispatching, 1000);
102 return NS_OK;
105 NS_IMETHODIMP
106 ThreadEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
107 uint32_t aDelayMs) {
108 nsCOMPtr<nsIRunnable> event = aEvent;
109 NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);
111 RefPtr<DelayedRunnable> r =
112 new DelayedRunnable(do_AddRef(this), event.forget(), aDelayMs);
113 nsresult rv = r->Init();
114 NS_ENSURE_SUCCESS(rv, rv);
116 return Dispatch(r.forget(), NS_DISPATCH_NORMAL);
119 NS_IMETHODIMP
120 ThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
121 *aIsOnCurrentThread = IsOnCurrentThread();
122 return NS_OK;
125 NS_IMETHODIMP_(bool)
126 ThreadEventTarget::IsOnCurrentThreadInfallible() {
127 // This method is only going to be called if `mThread` is null, which
128 // only happens when the thread has exited the event loop. Therefore, when
129 // we are called, we can never be on this thread.
130 return false;
133 void ThreadEventTarget::OnDelayedRunnableCreated(DelayedRunnable* aRunnable) {}
135 void ThreadEventTarget::OnDelayedRunnableScheduled(DelayedRunnable* aRunnable) {
136 MOZ_ASSERT(IsOnCurrentThread());
137 mScheduledDelayedRunnables.AppendElement(aRunnable);
140 void ThreadEventTarget::OnDelayedRunnableRan(DelayedRunnable* aRunnable) {
141 MOZ_ASSERT(IsOnCurrentThread());
142 Unused << mScheduledDelayedRunnables.RemoveElement(aRunnable);
145 void ThreadEventTarget::NotifyShutdown() {
146 MOZ_ASSERT(IsOnCurrentThread());
147 for (const auto& runnable : mScheduledDelayedRunnables) {
148 runnable->CancelTimer();
150 mScheduledDelayedRunnables.Clear();