Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/AbstractThread.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/AbstractThread.h"
8
9
#include "mozilla/ClearOnShutdown.h"
10
#include "mozilla/Maybe.h"
11
#include "mozilla/MozPromise.h" // We initialize the MozPromise logging in this file.
12
#include "mozilla/StaticPtr.h"
13
#include "mozilla/StateWatching.h" // We initialize the StateWatching logging in this file.
14
#include "mozilla/TaskQueue.h"
15
#include "mozilla/TaskDispatcher.h"
16
#include "mozilla/Unused.h"
17
18
#include "nsThreadUtils.h"
19
#include "nsContentUtils.h"
20
#include "nsServiceManagerUtils.h"
21
22
23
namespace mozilla {
24
25
LazyLogModule gMozPromiseLog("MozPromise");
26
LazyLogModule gStateWatchingLog("StateWatching");
27
28
StaticRefPtr<AbstractThread> sMainThread;
29
MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS;
30
31
class EventTargetWrapper : public AbstractThread
32
{
33
public:
34
  explicit EventTargetWrapper(nsIEventTarget* aTarget, bool aRequireTailDispatch)
35
    : AbstractThread(aRequireTailDispatch)
36
    , mTarget(aTarget)
37
3
  {
38
3
    // Our current mechanism of implementing tail dispatch is appshell-specific.
39
3
    // This is because a very similar mechanism already exists on the main
40
3
    // thread, and we want to avoid making event dispatch on the main thread
41
3
    // more complicated than it already is.
42
3
    //
43
3
    // If you need to use tail dispatch on other XPCOM threads, you'll need to
44
3
    // implement an nsIThreadObserver to fire the tail dispatcher at the
45
3
    // appropriate times. You will also need to modify this assertion.
46
3
    MOZ_ASSERT_IF(aRequireTailDispatch, NS_IsMainThread() && aTarget->IsOnCurrentThread());
47
3
  }
48
49
  virtual nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable,
50
                            DispatchReason aReason = NormalDispatch) override
51
3
  {
52
3
    AbstractThread* currentThread;
53
3
    if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
54
0
      return currentThread->TailDispatcher().AddTask(this, std::move(aRunnable));
55
0
    }
56
3
57
3
    RefPtr<nsIRunnable> runner(new Runner(this, std::move(aRunnable), false /* already drained by TaskGroupRunnable  */));
58
3
    return mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
59
3
  }
60
61
  // Prevent a GCC warning about the other overload of Dispatch being hidden.
62
  using AbstractThread::Dispatch;
63
64
  virtual bool IsCurrentThreadIn() override
65
0
  {
66
0
    return mTarget->IsOnCurrentThread();
67
0
  }
68
69
  void FireTailDispatcher()
70
0
  {
71
0
    AutoEnter context(this);
72
0
73
0
    MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
74
0
    mTailDispatcher.ref().DrainDirectTasks();
75
0
    mTailDispatcher.reset();
76
0
  }
77
78
  virtual TaskDispatcher& TailDispatcher() override
79
0
  {
80
0
    MOZ_ASSERT(IsCurrentThreadIn());
81
0
    if (!mTailDispatcher.isSome()) {
82
0
      mTailDispatcher.emplace(/* aIsTailDispatcher = */ true);
83
0
84
0
      nsCOMPtr<nsIRunnable> event =
85
0
        NewRunnableMethod("EventTargetWrapper::FireTailDispatcher",
86
0
                          this,
87
0
                          &EventTargetWrapper::FireTailDispatcher);
88
0
      nsContentUtils::RunInStableState(event.forget());
89
0
    }
90
0
91
0
    return mTailDispatcher.ref();
92
0
  }
93
94
  virtual bool MightHaveTailTasks() override
95
0
  {
96
0
    return mTailDispatcher.isSome();
97
0
  }
98
99
0
  virtual nsIEventTarget* AsEventTarget() override { return mTarget; }
100
101
private:
102
  nsCOMPtr<nsIThread> mRunningThread;
103
  RefPtr<nsIEventTarget> mTarget;
104
  Maybe<AutoTaskDispatcher> mTailDispatcher;
105
106
  virtual already_AddRefed<nsIRunnable>
107
  CreateDirectTaskDrainer(already_AddRefed<nsIRunnable> aRunnable) override
108
0
  {
109
0
    RefPtr<Runner> runner =
110
0
      new Runner(this, std::move(aRunnable), /* aDrainDirectTasks */ true);
111
0
    return runner.forget();
112
0
  }
113
114
  class Runner : public CancelableRunnable {
115
    class MOZ_STACK_CLASS AutoTaskGuard final {
116
    public:
117
      explicit AutoTaskGuard(EventTargetWrapper* aThread)
118
        : mLastCurrentThread(nullptr)
119
0
      {
120
0
        MOZ_ASSERT(aThread);
121
0
        mLastCurrentThread = sCurrentThreadTLS.get();
122
0
        sCurrentThreadTLS.set(aThread);
123
0
      }
124
125
      ~AutoTaskGuard()
126
0
      {
127
0
        sCurrentThreadTLS.set(mLastCurrentThread);
128
0
      }
129
    private:
130
      AbstractThread* mLastCurrentThread;
131
    };
132
133
  public:
134
    explicit Runner(EventTargetWrapper* aThread,
135
                    already_AddRefed<nsIRunnable> aRunnable,
136
                    bool aDrainDirectTasks)
137
      : CancelableRunnable("EventTargetWrapper::Runner")
138
      , mThread(aThread)
139
      , mRunnable(aRunnable)
140
      , mDrainDirectTasks(aDrainDirectTasks)
141
3
    {
142
3
    }
143
144
    NS_IMETHOD Run() override
145
0
    {
146
0
      AutoTaskGuard taskGuard(mThread);
147
0
148
0
      MOZ_ASSERT(mThread == AbstractThread::GetCurrent());
149
0
      MOZ_ASSERT(mThread->IsCurrentThreadIn());
150
0
      nsresult rv = mRunnable->Run();
151
0
152
0
      if (mDrainDirectTasks) {
153
0
        mThread->TailDispatcher().DrainDirectTasks();
154
0
      }
155
0
156
0
      return rv;
157
0
    }
158
159
    nsresult Cancel() override
160
0
    {
161
0
      // Set the TLS during Cancel() just in case it calls Run().
162
0
      AutoTaskGuard taskGuard(mThread);
163
0
164
0
      nsresult rv = NS_OK;
165
0
166
0
      // Try to cancel the runnable if it implements the right interface.
167
0
      // Otherwise just skip the runnable.
168
0
      nsCOMPtr<nsICancelableRunnable> cr = do_QueryInterface(mRunnable);
169
0
      if (cr) {
170
0
        rv = cr->Cancel();
171
0
      }
172
0
173
0
      return rv;
174
0
    }
175
176
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
177
    NS_IMETHOD GetName(nsACString& aName) override
178
0
    {
179
0
      aName.AssignLiteral("AbstractThread::Runner");
180
0
      if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) {
181
0
        nsAutoCString name;
182
0
        named->GetName(name);
183
0
        if (!name.IsEmpty()) {
184
0
          aName.AppendLiteral(" for ");
185
0
          aName.Append(name);
186
0
        }
187
0
      }
188
0
      return NS_OK;
189
0
    }
190
#endif
191
192
  private:
193
    RefPtr<EventTargetWrapper> mThread;
194
    RefPtr<nsIRunnable> mRunnable;
195
    bool mDrainDirectTasks;
196
  };
197
};
198
199
NS_IMPL_ISUPPORTS(AbstractThread, nsIEventTarget, nsISerialEventTarget)
200
201
NS_IMETHODIMP_(bool)
202
AbstractThread::IsOnCurrentThreadInfallible()
203
0
{
204
0
  return IsCurrentThreadIn();
205
0
}
206
207
NS_IMETHODIMP
208
AbstractThread::IsOnCurrentThread(bool* aResult)
209
0
{
210
0
  *aResult = IsCurrentThreadIn();
211
0
  return NS_OK;
212
0
}
213
214
NS_IMETHODIMP
215
AbstractThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
216
0
{
217
0
  nsCOMPtr<nsIRunnable> event(aEvent);
218
0
  return Dispatch(event.forget(), aFlags);
219
0
}
220
221
NS_IMETHODIMP
222
AbstractThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
223
0
{
224
0
  return Dispatch(std::move(aEvent), NormalDispatch);
225
0
}
226
227
NS_IMETHODIMP
228
AbstractThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
229
                                 uint32_t aDelayMs)
230
0
{
231
0
  return NS_ERROR_NOT_IMPLEMENTED;
232
0
}
233
234
nsresult
235
AbstractThread::TailDispatchTasksFor(AbstractThread* aThread)
236
0
{
237
0
  if (MightHaveTailTasks()) {
238
0
    return TailDispatcher().DispatchTasksFor(aThread);
239
0
  }
240
0
241
0
  return NS_OK;
242
0
}
243
244
bool
245
AbstractThread::HasTailTasksFor(AbstractThread* aThread)
246
0
{
247
0
  if (!MightHaveTailTasks()) {
248
0
    return false;
249
0
  }
250
0
  return TailDispatcher().HasTasksFor(aThread);
251
0
}
252
253
bool
254
AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const
255
0
{
256
0
  MOZ_ASSERT(aThread);
257
0
  // We require tail dispatch if both the source and destination
258
0
  // threads support it.
259
0
  return SupportsTailDispatch() && aThread->SupportsTailDispatch();
260
0
}
261
262
bool
263
AbstractThread::RequiresTailDispatchFromCurrentThread() const
264
0
{
265
0
  AbstractThread* current = GetCurrent();
266
0
  return current && RequiresTailDispatch(current);
267
0
}
268
269
AbstractThread*
270
AbstractThread::MainThread()
271
3
{
272
3
  MOZ_ASSERT(sMainThread);
273
3
  return sMainThread;
274
3
}
275
276
void
277
AbstractThread::InitTLS()
278
3
{
279
3
  if (!sCurrentThreadTLS.init()) {
280
0
    MOZ_CRASH();
281
0
  }
282
3
}
283
284
void
285
AbstractThread::InitMainThread()
286
3
{
287
3
  MOZ_ASSERT(NS_IsMainThread());
288
3
  MOZ_ASSERT(!sMainThread);
289
3
  nsCOMPtr<nsIThread> mainThread;
290
3
  NS_GetMainThread(getter_AddRefs(mainThread));
291
3
  MOZ_DIAGNOSTIC_ASSERT(mainThread);
292
3
  sMainThread = new EventTargetWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
293
3
  ClearOnShutdown(&sMainThread);
294
3
295
3
  if (!sCurrentThreadTLS.init()) {
296
0
    MOZ_CRASH();
297
0
  }
298
3
}
299
300
void
301
AbstractThread::DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable)
302
0
{
303
0
  GetCurrent()->TailDispatcher().AddStateChangeTask(this, std::move(aRunnable));
304
0
}
305
306
/* static */ void
307
AbstractThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable)
308
0
{
309
0
  GetCurrent()->TailDispatcher().AddDirectTask(std::move(aRunnable));
310
0
}
311
312
/* static */
313
already_AddRefed<AbstractThread>
314
AbstractThread::CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch)
315
0
{
316
0
  RefPtr<EventTargetWrapper> wrapper = new EventTargetWrapper(aThread, aRequireTailDispatch);
317
0
318
0
  bool onCurrentThread = false;
319
0
  Unused << aThread->IsOnCurrentThread(&onCurrentThread);
320
0
321
0
  if (onCurrentThread) {
322
0
    sCurrentThreadTLS.set(wrapper);
323
0
    return wrapper.forget();
324
0
  }
325
0
326
0
  // Set the thread-local sCurrentThreadTLS to point to the wrapper on the
327
0
  // target thread. This ensures that sCurrentThreadTLS is as expected by
328
0
  // AbstractThread::GetCurrent() on the target thread.
329
0
  nsCOMPtr<nsIRunnable> r =
330
0
    NS_NewRunnableFunction("AbstractThread::CreateXPCOMThreadWrapper",
331
0
                           [wrapper]() { sCurrentThreadTLS.set(wrapper); });
332
0
  aThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
333
0
  return wrapper.forget();
334
0
}
335
336
/* static  */
337
already_AddRefed<AbstractThread>
338
AbstractThread::CreateEventTargetWrapper(nsIEventTarget* aEventTarget,
339
                                         bool aRequireTailDispatch)
340
0
{
341
0
  MOZ_ASSERT(aEventTarget);
342
0
  nsCOMPtr<nsIThread> thread(do_QueryInterface(aEventTarget));
343
0
  Unused << thread; // simpler than DebugOnly<nsCOMPtr<nsIThread>>
344
0
  MOZ_ASSERT(!thread, "nsIThread should be wrapped by CreateXPCOMThreadWrapper!");
345
0
346
0
  RefPtr<EventTargetWrapper> wrapper =
347
0
    new EventTargetWrapper(aEventTarget, aRequireTailDispatch);
348
0
349
0
  return wrapper.forget();
350
0
}
351
352
} // namespace mozilla