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"
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
,
26 : mSink(aSink
), mIsMainThread(aIsMainThread
) {
27 mThread
= PR_GetCurrentThread();
30 ThreadEventTarget::~ThreadEventTarget() {
31 MOZ_ASSERT(mScheduledDelayedRunnables
.IsEmpty());
34 void ThreadEventTarget::SetCurrentThread(PRThread
* aThread
) {
38 void ThreadEventTarget::ClearCurrentThread() { mThread
= nullptr; }
40 NS_IMPL_ISUPPORTS(ThreadEventTarget
, nsIEventTarget
, nsISerialEventTarget
,
41 nsIDelayedRunnableObserver
)
44 ThreadEventTarget::DispatchFromScript(nsIRunnable
* aRunnable
, uint32_t aFlags
) {
45 return Dispatch(do_AddRef(aRunnable
), aFlags
);
49 ThreadEventTarget::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
,
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
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!
89 "ThreadEventTarget::Dispatch"_ns
,
90 [&, wrapper
]() -> bool { return !wrapper
->IsPending(); });
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);
106 ThreadEventTarget::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
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
);
120 ThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread
) {
121 *aIsOnCurrentThread
= IsOnCurrentThread();
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.
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();