Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/nsTimerImpl.h
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
#ifndef nsTimerImpl_h___
8
#define nsTimerImpl_h___
9
10
#include "nsITimer.h"
11
#include "nsIEventTarget.h"
12
#include "nsIObserver.h"
13
14
#include "nsCOMPtr.h"
15
16
#include "mozilla/Attributes.h"
17
#include "mozilla/Logging.h"
18
#include "mozilla/Mutex.h"
19
#include "mozilla/TimeStamp.h"
20
#include "mozilla/Variant.h"
21
22
#ifdef MOZ_TASK_TRACER
23
#include "TracedTaskCommon.h"
24
#endif
25
26
extern mozilla::LogModule* GetTimerLog();
27
28
#define NS_TIMER_CID \
29
{ /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */         \
30
     0x5ff24248,                                     \
31
     0x1dd2,                                         \
32
     0x11b2,                                         \
33
    {0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8} \
34
}
35
36
class nsTimerImplHolder;
37
38
// TimerThread, nsTimerEvent, and nsTimer have references to these. nsTimer has
39
// a separate lifecycle so we can Cancel() the underlying timer when the user of
40
// the nsTimer has let go of its last reference.
41
class nsTimerImpl
42
{
43
  ~nsTimerImpl()
44
0
  {
45
0
    MOZ_ASSERT(!mHolder);
46
0
  }
47
48
public:
49
  typedef mozilla::TimeStamp TimeStamp;
50
51
  explicit nsTimerImpl(nsITimer* aTimer);
52
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl)
53
  NS_DECL_NON_VIRTUAL_NSITIMER
54
55
  static nsresult Startup();
56
  static void Shutdown();
57
58
  void SetDelayInternal(uint32_t aDelay, TimeStamp aBase = TimeStamp::Now());
59
  void CancelImpl(bool aClearITimer);
60
61
  void Fire(int32_t aGeneration);
62
63
#ifdef MOZ_TASK_TRACER
64
  void GetTLSTraceInfo();
65
  mozilla::tasktracer::TracedTaskCommon GetTracedTask();
66
#endif
67
68
  int32_t GetGeneration()
69
86
  {
70
86
    return mGeneration;
71
86
  }
72
73
  struct Callback {
74
    Callback() :
75
      mType(Type::Unknown),
76
      mName(Nothing),
77
      mClosure(nullptr)
78
464
    {
79
464
      mCallback.c = nullptr;
80
464
    }
81
82
    Callback(const Callback& other) = delete;
83
    Callback& operator=(const Callback& other) = delete;
84
85
    ~Callback()
86
258
    {
87
258
      if (mType == Type::Interface) {
88
0
        NS_RELEASE(mCallback.i);
89
258
      } else if (mType == Type::Observer) {
90
0
        NS_RELEASE(mCallback.o);
91
0
      }
92
258
    }
93
94
    void swap(Callback& other)
95
258
    {
96
258
      std::swap(mType, other.mType);
97
258
      std::swap(mCallback, other.mCallback);
98
258
      std::swap(mName, other.mName);
99
258
      std::swap(mClosure, other.mClosure);
100
258
    }
101
102
    enum class Type : uint8_t {
103
      Unknown = 0,
104
      Interface = 1,
105
      Function = 2,
106
      Observer = 3,
107
    };
108
    Type mType;
109
110
    union CallbackUnion
111
    {
112
      nsTimerCallbackFunc c;
113
      // These refcounted references are managed manually, as they are in a union
114
      nsITimerCallback* MOZ_OWNING_REF i;
115
      nsIObserver* MOZ_OWNING_REF o;
116
    } mCallback;
117
118
    // |Name| is a tagged union type representing one of (a) nothing, (b) a
119
    // string, or (c) a function. mozilla::Variant doesn't naturally handle the
120
    // "nothing" case, so we define a dummy type and value (which is unused and
121
    // so the exact value doesn't matter) for it.
122
    typedef const int NameNothing;
123
    typedef const char* NameString;
124
    typedef nsTimerNameCallbackFunc NameFunc;
125
    typedef mozilla::Variant<NameNothing, NameString, NameFunc> Name;
126
    static const NameNothing Nothing;
127
    Name mName;
128
129
    void*                 mClosure;
130
  };
131
132
  nsresult InitCommon(uint32_t aDelayMS, uint32_t aType,
133
                      Callback&& newCallback);
134
135
  nsresult InitCommon(const mozilla::TimeDuration& aDelay, uint32_t aType,
136
                      Callback&& newCallback);
137
138
  Callback& GetCallback()
139
0
  {
140
0
    mMutex.AssertCurrentThreadOwns();
141
0
    if (mCallback.mType == Callback::Type::Unknown) {
142
0
      return mCallbackDuringFire;
143
0
    }
144
0
145
0
    return mCallback;
146
0
  }
147
148
  bool IsRepeating() const
149
0
  {
150
0
    static_assert(nsITimer::TYPE_ONE_SHOT < nsITimer::TYPE_REPEATING_SLACK,
151
0
                  "invalid ordering of timer types!");
152
0
    static_assert(
153
0
        nsITimer::TYPE_REPEATING_SLACK < nsITimer::TYPE_REPEATING_PRECISE,
154
0
        "invalid ordering of timer types!");
155
0
    static_assert(
156
0
        nsITimer::TYPE_REPEATING_PRECISE <
157
0
          nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
158
0
        "invalid ordering of timer types!");
159
0
    return mType >= nsITimer::TYPE_REPEATING_SLACK &&
160
0
           mType < nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY;
161
0
  }
162
163
  bool IsLowPriority() const
164
0
  {
165
0
    return mType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY ||
166
0
           mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
167
0
  }
168
169
  bool IsSlack() const
170
0
  {
171
0
    return mType == nsITimer::TYPE_REPEATING_SLACK ||
172
0
           mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
173
0
  }
174
175
  void GetName(nsACString& aName);
176
177
  void SetHolder(nsTimerImplHolder* aHolder);
178
179
  nsCOMPtr<nsIEventTarget> mEventTarget;
180
181
  void LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay);
182
183
  nsresult InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
184
                                      void* aClosure,
185
                                      uint32_t aDelay,
186
                                      uint32_t aType,
187
                                      const Callback::Name& aName);
188
189
  // This weak reference must be cleared by the nsTimerImplHolder by calling
190
  // SetHolder(nullptr) before the holder is destroyed.
191
  nsTimerImplHolder*    mHolder;
192
193
  // These members are set by the initiating thread, when the timer's type is
194
  // changed and during the period where it fires on that thread.
195
  uint8_t               mType;
196
197
  // The generation number of this timer, re-generated each time the timer is
198
  // initialized so one-shot timers can be canceled and re-initialized by the
199
  // arming thread without any bad race conditions.
200
  // Updated only after this timer has been removed from the timer thread.
201
  int32_t               mGeneration;
202
203
  mozilla::TimeDuration mDelay;
204
  // Updated only after this timer has been removed from the timer thread.
205
  mozilla::TimeStamp    mTimeout;
206
207
#ifdef MOZ_TASK_TRACER
208
  mozilla::tasktracer::TracedTaskCommon mTracedTask;
209
#endif
210
211
  static double         sDeltaSum;
212
  static double         sDeltaSumSquared;
213
  static double         sDeltaNum;
214
  RefPtr<nsITimer>      mITimer;
215
  mozilla::Mutex mMutex;
216
  Callback              mCallback;
217
  Callback              mCallbackDuringFire;
218
};
219
220
class nsTimer final : public nsITimer
221
{
222
  virtual ~nsTimer();
223
public:
224
103
  nsTimer() : mImpl(new nsTimerImpl(this)) {}
225
226
  friend class TimerThread;
227
  friend class nsTimerEvent;
228
229
  NS_DECL_THREADSAFE_ISUPPORTS
230
  NS_FORWARD_SAFE_NSITIMER(mImpl);
231
232
  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
233
234
private:
235
  // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
236
  // null this to break the cycle.
237
  RefPtr<nsTimerImpl> mImpl;
238
};
239
240
// A class that holds on to an nsTimerImpl.  This lets the nsTimerImpl object
241
// directly instruct its holder to forget the timer, avoiding list lookups.
242
class nsTimerImplHolder
243
{
244
public:
245
  explicit nsTimerImplHolder(nsTimerImpl* aTimerImpl)
246
    : mTimerImpl(aTimerImpl)
247
90
  {
248
90
    if (mTimerImpl) {
249
90
      mTimerImpl->SetHolder(this);
250
90
    }
251
90
  }
252
253
  ~nsTimerImplHolder()
254
90
  {
255
90
    if (mTimerImpl) {
256
0
      mTimerImpl->SetHolder(nullptr);
257
0
    }
258
90
  }
259
260
  void
261
  Forget(nsTimerImpl* aTimerImpl)
262
4
  {
263
4
    if (MOZ_UNLIKELY(!mTimerImpl)) {
264
0
      return;
265
0
    }
266
4
    MOZ_ASSERT(aTimerImpl == mTimerImpl);
267
4
    mTimerImpl->SetHolder(nullptr);
268
4
    mTimerImpl = nullptr;
269
4
  }
270
271
protected:
272
  RefPtr<nsTimerImpl> mTimerImpl;
273
};
274
275
276
#endif /* nsTimerImpl_h___ */