Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/SchedulerGroup.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "mozilla/SchedulerGroup.h"
8
9
#include "jsfriendapi.h"
10
#include "mozilla/AbstractThread.h"
11
#include "mozilla/Atomics.h"
12
#include "mozilla/Move.h"
13
#include "mozilla/Unused.h"
14
#include "mozilla/dom/DocGroup.h"
15
#include "nsINamed.h"
16
#include "nsQueryObject.h"
17
#include "mozilla/dom/ScriptSettings.h"
18
#include "nsThreadUtils.h"
19
20
#include "mozilla/Telemetry.h"
21
22
using namespace mozilla;
23
24
/* SchedulerEventTarget */
25
26
namespace {
27
28
#define NS_DISPATCHEREVENTTARGET_IID \
29
{ 0xbf4e36c8, 0x7d04, 0x4ef4, \
30
  { 0xbb, 0xd8, 0x11, 0x09, 0x0a, 0xdb, 0x4d, 0xf7 } }
31
32
class SchedulerEventTarget final : public nsISerialEventTarget
33
{
34
  RefPtr<SchedulerGroup> mDispatcher;
35
  TaskCategory mCategory;
36
37
public:
38
  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DISPATCHEREVENTTARGET_IID)
39
40
  SchedulerEventTarget(SchedulerGroup* aDispatcher, TaskCategory aCategory)
41
   : mDispatcher(aDispatcher)
42
   , mCategory(aCategory)
43
27
  {}
44
45
  NS_DECL_THREADSAFE_ISUPPORTS
46
  NS_DECL_NSIEVENTTARGET_FULL
47
48
0
  SchedulerGroup* Dispatcher() const { return mDispatcher; }
49
50
private:
51
0
  ~SchedulerEventTarget() {}
52
};
53
54
NS_DEFINE_STATIC_IID_ACCESSOR(SchedulerEventTarget, NS_DISPATCHEREVENTTARGET_IID)
55
56
static Atomic<uint64_t> gEarliestUnprocessedVsync(0);
57
58
} // namespace
59
60
NS_IMPL_ISUPPORTS(SchedulerEventTarget,
61
                  SchedulerEventTarget,
62
                  nsIEventTarget,
63
                  nsISerialEventTarget)
64
65
NS_IMETHODIMP
66
SchedulerEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
67
0
{
68
0
  return Dispatch(do_AddRef(aRunnable), aFlags);
69
0
}
70
71
NS_IMETHODIMP
72
SchedulerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
73
84
{
74
84
  if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
75
0
    return NS_ERROR_UNEXPECTED;
76
0
  }
77
84
  return mDispatcher->Dispatch(mCategory, std::move(aRunnable));
78
84
}
79
80
NS_IMETHODIMP
81
SchedulerEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
82
0
{
83
0
  return NS_ERROR_NOT_IMPLEMENTED;
84
0
}
85
86
NS_IMETHODIMP
87
SchedulerEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
88
0
{
89
0
  *aIsOnCurrentThread = NS_IsMainThread();
90
0
  return NS_OK;
91
0
}
92
93
NS_IMETHODIMP_(bool)
94
SchedulerEventTarget::IsOnCurrentThreadInfallible()
95
0
{
96
0
  return NS_IsMainThread();
97
0
}
98
99
/* static */ nsresult
100
SchedulerGroup::UnlabeledDispatch(TaskCategory aCategory,
101
                                  already_AddRefed<nsIRunnable>&& aRunnable)
102
102
{
103
102
  if (NS_IsMainThread()) {
104
18
    return NS_DispatchToCurrentThread(std::move(aRunnable));
105
84
  } else {
106
84
    return NS_DispatchToMainThread(std::move(aRunnable));
107
84
  }
108
102
}
109
110
/* static */ void
111
SchedulerGroup::MarkVsyncReceived()
112
0
{
113
0
  if (gEarliestUnprocessedVsync) {
114
0
    // If we've seen a vsync already, but haven't handled it, keep the
115
0
    // older one.
116
0
    return;
117
0
  }
118
0
119
0
  MOZ_ASSERT(!NS_IsMainThread());
120
0
  bool inconsistent = false;
121
0
  TimeStamp creation = TimeStamp::ProcessCreation(&inconsistent);
122
0
  if (inconsistent) {
123
0
    return;
124
0
  }
125
0
126
0
  gEarliestUnprocessedVsync = (TimeStamp::Now() - creation).ToMicroseconds();
127
0
}
128
129
/* static */ void
130
SchedulerGroup::MarkVsyncRan()
131
0
{
132
0
  gEarliestUnprocessedVsync = 0;
133
0
}
134
135
MOZ_THREAD_LOCAL(bool) SchedulerGroup::sTlsValidatingAccess;
136
137
SchedulerGroup::SchedulerGroup()
138
 : mIsRunning(false)
139
3
{
140
3
  if (NS_IsMainThread()) {
141
3
    sTlsValidatingAccess.infallibleInit();
142
3
  }
143
3
}
144
145
nsresult
146
SchedulerGroup::DispatchWithDocGroup(TaskCategory aCategory,
147
                                     already_AddRefed<nsIRunnable>&& aRunnable,
148
                                     dom::DocGroup* aDocGroup)
149
0
{
150
0
  return LabeledDispatch(aCategory, std::move(aRunnable), aDocGroup);
151
0
}
152
153
nsresult
154
SchedulerGroup::Dispatch(TaskCategory aCategory,
155
                         already_AddRefed<nsIRunnable>&& aRunnable)
156
102
{
157
102
  return LabeledDispatch(aCategory, std::move(aRunnable), nullptr);
158
102
}
159
160
nsISerialEventTarget*
161
SchedulerGroup::EventTargetFor(TaskCategory aCategory) const
162
84
{
163
84
  MOZ_ASSERT(aCategory != TaskCategory::Count);
164
84
  MOZ_ASSERT(mEventTargets[size_t(aCategory)]);
165
84
  return mEventTargets[size_t(aCategory)];
166
84
}
167
168
AbstractThread*
169
SchedulerGroup::AbstractMainThreadFor(TaskCategory aCategory)
170
0
{
171
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
172
0
  return AbstractMainThreadForImpl(aCategory);
173
0
}
174
175
AbstractThread*
176
SchedulerGroup::AbstractMainThreadForImpl(TaskCategory aCategory)
177
0
{
178
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
179
0
  MOZ_ASSERT(aCategory != TaskCategory::Count);
180
0
  MOZ_ASSERT(mEventTargets[size_t(aCategory)]);
181
0
182
0
  if (!mAbstractThreads[size_t(aCategory)]) {
183
0
    mAbstractThreads[size_t(aCategory)] =
184
0
      AbstractThread::CreateEventTargetWrapper(mEventTargets[size_t(aCategory)],
185
0
                                               /* aDrainDirectTasks = */ true);
186
0
  }
187
0
188
0
  return mAbstractThreads[size_t(aCategory)];
189
0
}
190
191
void
192
SchedulerGroup::CreateEventTargets(bool aNeedValidation)
193
3
{
194
30
  for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
195
27
    TaskCategory category = static_cast<TaskCategory>(i);
196
27
    if (!aNeedValidation) {
197
0
      // The chrome TabGroup dispatches directly to the main thread. This means
198
0
      // that we don't have to worry about cyclical references when cleaning up
199
0
      // the chrome TabGroup.
200
0
      mEventTargets[i] = GetMainThreadSerialEventTarget();
201
27
    } else {
202
27
      mEventTargets[i] = CreateEventTargetFor(category);
203
27
    }
204
27
  }
205
3
}
206
207
void
208
SchedulerGroup::Shutdown(bool aXPCOMShutdown)
209
0
{
210
0
  // There is a RefPtr cycle TabGroup -> SchedulerEventTarget -> TabGroup. To
211
0
  // avoid leaks, we need to break the chain somewhere. We shouldn't be using
212
0
  // the ThrottledEventQueue for this TabGroup when no windows belong to it,
213
0
  // so it's safe to null out the queue here.
214
0
  for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
215
0
    mEventTargets[i] = aXPCOMShutdown ? nullptr : GetMainThreadSerialEventTarget();
216
0
    mAbstractThreads[i] = nullptr;
217
0
  }
218
0
}
219
220
already_AddRefed<nsISerialEventTarget>
221
SchedulerGroup::CreateEventTargetFor(TaskCategory aCategory)
222
27
{
223
27
  RefPtr<SchedulerEventTarget> target =
224
27
    new SchedulerEventTarget(this, aCategory);
225
27
  return target.forget();
226
27
}
227
228
/* static */ SchedulerGroup*
229
SchedulerGroup::FromEventTarget(nsIEventTarget* aEventTarget)
230
0
{
231
0
  RefPtr<SchedulerEventTarget> target = do_QueryObject(aEventTarget);
232
0
  if (!target) {
233
0
    return nullptr;
234
0
  }
235
0
  return target->Dispatcher();
236
0
}
237
238
nsresult
239
SchedulerGroup::LabeledDispatch(TaskCategory aCategory,
240
                                already_AddRefed<nsIRunnable>&& aRunnable,
241
                                dom::DocGroup* aDocGroup)
242
102
{
243
102
  nsCOMPtr<nsIRunnable> runnable(aRunnable);
244
102
  if (XRE_IsContentProcess()) {
245
0
    RefPtr<Runnable> internalRunnable = new Runnable(runnable.forget(), this, aDocGroup);
246
0
    return InternalUnlabeledDispatch(aCategory, internalRunnable.forget());
247
0
  }
248
102
  return UnlabeledDispatch(aCategory, runnable.forget());
249
102
}
250
251
/*static*/ nsresult
252
SchedulerGroup::InternalUnlabeledDispatch(TaskCategory aCategory,
253
                                          already_AddRefed<Runnable>&& aRunnable)
254
0
{
255
0
  if (NS_IsMainThread()) {
256
0
    // NS_DispatchToCurrentThread will not leak the passed in runnable
257
0
    // when it fails, so we don't need to do anything special.
258
0
    return NS_DispatchToCurrentThread(std::move(aRunnable));
259
0
  }
260
0
261
0
  RefPtr<Runnable> runnable(aRunnable);
262
0
  nsresult rv = NS_DispatchToMainThread(do_AddRef(runnable));
263
0
  if (NS_FAILED(rv)) {
264
0
    // Dispatch failed.  This is a situation where we would have used
265
0
    // NS_DispatchToMainThread rather than calling into the SchedulerGroup
266
0
    // machinery, and the caller would be expecting to leak the nsIRunnable
267
0
    // originally passed in.  But because we've had to wrap things up
268
0
    // internally, we were going to leak the nsIRunnable *and* our Runnable
269
0
    // wrapper.  But there's no reason that we have to leak our Runnable
270
0
    // wrapper; we can just leak the wrapped nsIRunnable, and let the caller
271
0
    // take care of unleaking it if they need to.
272
0
    Unused << runnable->mRunnable.forget().take();
273
0
    nsrefcnt refcnt = runnable.get()->Release();
274
0
    MOZ_RELEASE_ASSERT(refcnt == 1, "still holding an unexpected reference!");
275
0
  }
276
0
277
0
  return rv;
278
0
}
279
280
/* static */ void
281
SchedulerGroup::SetValidatingAccess(ValidationType aType)
282
0
{
283
0
  bool validating = aType == StartValidation;
284
0
  sTlsValidatingAccess.set(validating);
285
0
286
0
  dom::AutoJSAPI jsapi;
287
0
  jsapi.Init();
288
0
  js::EnableAccessValidation(jsapi.cx(), validating);
289
0
}
290
291
SchedulerGroup::Runnable::Runnable(already_AddRefed<nsIRunnable>&& aRunnable,
292
                                   SchedulerGroup* aGroup,
293
                                   dom::DocGroup* aDocGroup)
294
  : mozilla::Runnable("SchedulerGroup::Runnable")
295
  , mRunnable(std::move(aRunnable))
296
  , mGroup(aGroup)
297
  , mDocGroup(aDocGroup)
298
0
{
299
0
}
300
301
bool
302
SchedulerGroup::Runnable::GetAffectedSchedulerGroups(SchedulerGroupSet& aGroups)
303
0
{
304
0
  aGroups.Clear();
305
0
  aGroups.Put(Group());
306
0
  return true;
307
0
}
308
309
dom::DocGroup*
310
SchedulerGroup::Runnable::DocGroup() const
311
0
{
312
0
  return mDocGroup;
313
0
}
314
315
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
316
NS_IMETHODIMP
317
SchedulerGroup::Runnable::GetName(nsACString& aName)
318
0
{
319
0
  // Try to get a name from the underlying runnable.
320
0
  nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable);
321
0
  if (named) {
322
0
    named->GetName(aName);
323
0
  }
324
0
  if (aName.IsEmpty()) {
325
0
    aName.AssignLiteral("anonymous");
326
0
  }
327
0
328
0
  return NS_OK;
329
0
}
330
#endif
331
332
NS_IMETHODIMP
333
SchedulerGroup::Runnable::Run()
334
0
{
335
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
336
0
337
0
  nsresult result = mRunnable->Run();
338
0
339
0
  // The runnable's destructor can have side effects, so try to execute it in
340
0
  // the scope of the TabGroup.
341
0
  mRunnable = nullptr;
342
0
343
0
  mGroup->SetValidatingAccess(EndValidation);
344
0
  return result;
345
0
}
346
347
NS_IMETHODIMP
348
SchedulerGroup::Runnable::GetPriority(uint32_t* aPriority)
349
0
{
350
0
  *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
351
0
  nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(mRunnable);
352
0
  return runnablePrio ? runnablePrio->GetPriority(aPriority) : NS_OK;
353
0
}
354
355
NS_IMPL_ISUPPORTS_INHERITED(SchedulerGroup::Runnable,
356
                            mozilla::Runnable,
357
                            nsIRunnablePriority,
358
                            nsILabelableRunnable,
359
                            SchedulerGroup::Runnable)