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 "mozilla/SchedulerGroup.h"
11 #include "jsfriendapi.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/Telemetry.h"
14 #include "mozilla/Unused.h"
15 #include "mozilla/dom/DocGroup.h"
16 #include "mozilla/dom/ScriptSettings.h"
18 #include "nsQueryObject.h"
19 #include "nsThreadUtils.h"
21 using namespace mozilla
;
25 static Atomic
<uint64_t> gEarliestUnprocessedVsync(0);
30 nsresult
SchedulerGroup::UnlabeledDispatch(
31 TaskCategory aCategory
, already_AddRefed
<nsIRunnable
>&& aRunnable
) {
32 if (NS_IsMainThread()) {
33 return NS_DispatchToCurrentThread(std::move(aRunnable
));
35 return NS_DispatchToMainThread(std::move(aRunnable
));
40 void SchedulerGroup::MarkVsyncReceived() {
41 // May be called on any thread when a vsync is received and scheduled to be
42 // processed. This may occur on the main thread due to queued messages when
43 // the channel is connected.
44 TimeStamp creation
= TimeStamp::ProcessCreation();
46 // Attempt to set gEarliestUnprocessedVsync to our new value. If we've seen a
47 // vsync already, but haven't handled it, the `compareExchange` will fail and
48 // the static won't be updated.
49 uint64_t unprocessedVsync
=
50 uint64_t((TimeStamp::Now() - creation
).ToMicroseconds());
51 gEarliestUnprocessedVsync
.compareExchange(0, unprocessedVsync
);
55 void SchedulerGroup::MarkVsyncRan() { gEarliestUnprocessedVsync
= 0; }
57 SchedulerGroup::SchedulerGroup() : mIsRunning(false) {}
60 nsresult
SchedulerGroup::Dispatch(TaskCategory aCategory
,
61 already_AddRefed
<nsIRunnable
>&& aRunnable
) {
62 return LabeledDispatch(aCategory
, std::move(aRunnable
), nullptr);
66 nsresult
SchedulerGroup::LabeledDispatch(
67 TaskCategory aCategory
, already_AddRefed
<nsIRunnable
>&& aRunnable
,
68 mozilla::PerformanceCounter
* aPerformanceCounter
) {
69 nsCOMPtr
<nsIRunnable
> runnable(aRunnable
);
70 if (XRE_IsContentProcess()) {
71 RefPtr
<Runnable
> internalRunnable
=
72 new Runnable(runnable
.forget(), aPerformanceCounter
);
73 return InternalUnlabeledDispatch(aCategory
, internalRunnable
.forget());
75 return UnlabeledDispatch(aCategory
, runnable
.forget());
79 nsresult
SchedulerGroup::InternalUnlabeledDispatch(
80 TaskCategory aCategory
, already_AddRefed
<Runnable
>&& aRunnable
) {
81 if (NS_IsMainThread()) {
82 // NS_DispatchToCurrentThread will not leak the passed in runnable
83 // when it fails, so we don't need to do anything special.
84 return NS_DispatchToCurrentThread(std::move(aRunnable
));
87 RefPtr
<Runnable
> runnable(aRunnable
);
88 nsresult rv
= NS_DispatchToMainThread(do_AddRef(runnable
));
90 // Dispatch failed. This is a situation where we would have used
91 // NS_DispatchToMainThread rather than calling into the SchedulerGroup
92 // machinery, and the caller would be expecting to leak the nsIRunnable
93 // originally passed in. But because we've had to wrap things up
94 // internally, we were going to leak the nsIRunnable *and* our Runnable
95 // wrapper. But there's no reason that we have to leak our Runnable
96 // wrapper; we can just leak the wrapped nsIRunnable, and let the caller
97 // take care of unleaking it if they need to.
98 Unused
<< runnable
->mRunnable
.forget().take();
99 nsrefcnt refcnt
= runnable
.get()->Release();
100 MOZ_RELEASE_ASSERT(refcnt
== 1, "still holding an unexpected reference!");
106 SchedulerGroup::Runnable::Runnable(
107 already_AddRefed
<nsIRunnable
>&& aRunnable
,
108 mozilla::PerformanceCounter
* aPerformanceCounter
)
109 : mozilla::Runnable("SchedulerGroup::Runnable"),
110 mRunnable(std::move(aRunnable
)),
111 mPerformanceCounter(aPerformanceCounter
) {}
113 mozilla::PerformanceCounter
* SchedulerGroup::Runnable::GetPerformanceCounter()
115 return mPerformanceCounter
;
118 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
120 SchedulerGroup::Runnable::GetName(nsACString
& aName
) {
121 // Try to get a name from the underlying runnable.
122 nsCOMPtr
<nsINamed
> named
= do_QueryInterface(mRunnable
);
124 named
->GetName(aName
);
126 if (aName
.IsEmpty()) {
127 aName
.AssignLiteral("anonymous");
135 SchedulerGroup::Runnable::Run() {
136 MOZ_RELEASE_ASSERT(NS_IsMainThread());
137 // The runnable's destructor can have side effects, so try to execute it in
138 // the scope of the SchedulerGroup.
139 nsCOMPtr
<nsIRunnable
> runnable(std::move(mRunnable
));
140 return runnable
->Run();
144 SchedulerGroup::Runnable::GetPriority(uint32_t* aPriority
) {
145 *aPriority
= nsIRunnablePriority::PRIORITY_NORMAL
;
146 nsCOMPtr
<nsIRunnablePriority
> runnablePrio
= do_QueryInterface(mRunnable
);
147 return runnablePrio
? runnablePrio
->GetPriority(aPriority
) : NS_OK
;
150 NS_IMPL_ISUPPORTS_INHERITED(SchedulerGroup::Runnable
, mozilla::Runnable
,
151 nsIRunnablePriority
, SchedulerGroup::Runnable
)