Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/ThreadEventTarget.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 "ThreadEventTarget.h"
8
#include "mozilla/ThreadEventQueue.h"
9
10
#include "LeakRefPtr.h"
11
#include "mozilla/TimeStamp.h"
12
#include "nsComponentManagerUtils.h"
13
#include "nsITimer.h"
14
#include "nsThreadManager.h"
15
#include "nsThreadSyncDispatch.h"
16
#include "nsThreadUtils.h"
17
#include "nsXPCOMPrivate.h" // for gXPCOMThreadsShutDown
18
#include "ThreadDelay.h"
19
20
#ifdef MOZ_TASK_TRACER
21
#include "GeckoTaskTracer.h"
22
#include "TracedTaskCommon.h"
23
using namespace mozilla::tasktracer;
24
#endif
25
26
using namespace mozilla;
27
28
namespace {
29
30
class DelayedRunnable : public Runnable,
31
                        public nsITimerCallback
32
{
33
public:
34
  DelayedRunnable(already_AddRefed<nsIEventTarget> aTarget,
35
                  already_AddRefed<nsIRunnable> aRunnable,
36
                  uint32_t aDelay)
37
    : mozilla::Runnable("DelayedRunnable")
38
    , mTarget(aTarget)
39
    , mWrappedRunnable(aRunnable)
40
    , mDelayedFrom(TimeStamp::NowLoRes())
41
    , mDelay(aDelay)
42
0
  { }
43
44
  NS_DECL_ISUPPORTS_INHERITED
45
46
  nsresult Init()
47
0
  {
48
0
    return NS_NewTimerWithCallback(getter_AddRefs(mTimer),
49
0
                                   this, mDelay, nsITimer::TYPE_ONE_SHOT,
50
0
                                   mTarget);
51
0
  }
52
53
  nsresult DoRun()
54
0
  {
55
0
    nsCOMPtr<nsIRunnable> r = mWrappedRunnable.forget();
56
0
    return r->Run();
57
0
  }
58
59
  NS_IMETHOD Run() override
60
0
  {
61
0
    // Already ran?
62
0
    if (!mWrappedRunnable) {
63
0
      return NS_OK;
64
0
    }
65
0
66
0
    // Are we too early?
67
0
    if ((TimeStamp::NowLoRes() - mDelayedFrom).ToMilliseconds() < mDelay) {
68
0
      return NS_OK; // Let the nsITimer run us.
69
0
    }
70
0
71
0
    mTimer->Cancel();
72
0
    return DoRun();
73
0
  }
74
75
  NS_IMETHOD Notify(nsITimer* aTimer) override
76
0
  {
77
0
    // If we already ran, the timer should have been canceled.
78
0
    MOZ_ASSERT(mWrappedRunnable);
79
0
    MOZ_ASSERT(aTimer == mTimer);
80
0
81
0
    return DoRun();
82
0
  }
83
84
private:
85
0
  ~DelayedRunnable() {}
86
87
  nsCOMPtr<nsIEventTarget> mTarget;
88
  nsCOMPtr<nsIRunnable> mWrappedRunnable;
89
  nsCOMPtr<nsITimer> mTimer;
90
  TimeStamp mDelayedFrom;
91
  uint32_t mDelay;
92
};
93
94
NS_IMPL_ISUPPORTS_INHERITED(DelayedRunnable, Runnable, nsITimerCallback)
95
96
} // anonymous namespace
97
98
ThreadEventTarget::ThreadEventTarget(ThreadTargetSink* aSink,
99
                                     bool aIsMainThread)
100
  : mSink(aSink)
101
  , mIsMainThread(aIsMainThread)
102
46
{
103
46
  mVirtualThread = GetCurrentVirtualThread();
104
46
}
105
106
void
107
ThreadEventTarget::SetCurrentThread()
108
13
{
109
13
  mVirtualThread = GetCurrentVirtualThread();
110
13
}
111
112
NS_IMPL_ISUPPORTS(ThreadEventTarget,
113
                  nsIEventTarget,
114
                  nsISerialEventTarget)
115
116
NS_IMETHODIMP
117
ThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
118
0
{
119
0
  return Dispatch(do_AddRef(aRunnable), aFlags);
120
0
}
121
122
NS_IMETHODIMP
123
ThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
124
138
{
125
138
  // We want to leak the reference when we fail to dispatch it, so that
126
138
  // we won't release the event in a wrong thread.
127
138
  LeakRefPtr<nsIRunnable> event(std::move(aEvent));
128
138
  if (NS_WARN_IF(!event)) {
129
0
    return NS_ERROR_INVALID_ARG;
130
0
  }
131
138
132
138
  if (gXPCOMThreadsShutDown && !mIsMainThread) {
133
0
    NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
134
0
    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
135
0
  }
136
138
137
#ifdef MOZ_TASK_TRACER
138
  nsCOMPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event.take());
139
  (static_cast<TracedRunnable*>(tracedRunnable.get()))->DispatchTask();
140
  // XXX tracedRunnable will always leaked when we fail to disptch.
141
  event = tracedRunnable.forget();
142
#endif
143
144
138
  if (aFlags & DISPATCH_SYNC) {
145
0
    nsCOMPtr<nsIEventTarget> current = GetCurrentThreadEventTarget();
146
0
    if (NS_WARN_IF(!current)) {
147
0
      return NS_ERROR_NOT_AVAILABLE;
148
0
    }
149
0
150
0
    // XXX we should be able to do something better here... we should
151
0
    //     be able to monitor the slot occupied by this event and use
152
0
    //     that to tell us when the event has been processed.
153
0
154
0
    RefPtr<nsThreadSyncDispatch> wrapper =
155
0
      new nsThreadSyncDispatch(current.forget(), event.take());
156
0
    bool success = mSink->PutEvent(do_AddRef(wrapper), EventPriority::Normal); // hold a ref
157
0
    if (!success) {
158
0
      // PutEvent leaked the wrapper runnable object on failure, so we
159
0
      // explicitly release this object once for that. Note that this
160
0
      // object will be released again soon because it exits the scope.
161
0
      wrapper.get()->Release();
162
0
      return NS_ERROR_UNEXPECTED;
163
0
    }
164
0
165
0
    // Allows waiting; ensure no locks are held that would deadlock us!
166
0
    SpinEventLoopUntil([&, wrapper]() -> bool {
167
0
        return !wrapper->IsPending();
168
0
      });
169
0
170
0
    return NS_OK;
171
0
  }
172
138
173
138
  NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
174
138
               aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
175
138
  if (!mSink->PutEvent(event.take(), EventPriority::Normal)) {
176
0
    return NS_ERROR_UNEXPECTED;
177
0
  }
178
138
  // Delay to encourage the receiving task to run before we do work.
179
138
  DelayForChaosMode(ChaosFeature::TaskDispatching, 1000);
180
138
  return NS_OK;
181
138
}
182
183
NS_IMETHODIMP
184
ThreadEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs)
185
0
{
186
0
  NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);
187
0
188
0
  RefPtr<DelayedRunnable> r = new DelayedRunnable(do_AddRef(this),
189
0
                                                  std::move(aEvent),
190
0
                                                  aDelayMs);
191
0
  nsresult rv = r->Init();
192
0
  NS_ENSURE_SUCCESS(rv, rv);
193
0
194
0
  return Dispatch(r.forget(), NS_DISPATCH_NORMAL);
195
0
}
196
197
NS_IMETHODIMP
198
ThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
199
0
{
200
0
  *aIsOnCurrentThread = IsOnCurrentThread();
201
0
  return NS_OK;
202
0
}
203
204
NS_IMETHODIMP_(bool)
205
ThreadEventTarget::IsOnCurrentThreadInfallible()
206
0
{
207
0
  // Rely on mVirtualThread being correct.
208
0
  MOZ_CRASH("IsOnCurrentThreadInfallible should never be called on nsIThread");
209
0
}