Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/tests/gtest/TestTimers.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "nsIThread.h"
7
#include "nsITimer.h"
8
9
#include "nsCOMPtr.h"
10
#include "nsComponentManagerUtils.h"
11
#include "nsServiceManagerUtils.h"
12
#include "nsThreadUtils.h"
13
#include "prinrval.h"
14
#include "prmon.h"
15
#include "prthread.h"
16
#include "mozilla/Attributes.h"
17
18
#include "mozilla/ReentrantMonitor.h"
19
20
#include <list>
21
#include <vector>
22
23
#include "gtest/gtest.h"
24
25
using namespace mozilla;
26
27
typedef nsresult(*TestFuncPtr)();
28
29
class AutoTestThread
30
{
31
public:
32
0
  AutoTestThread() {
33
0
    nsCOMPtr<nsIThread> newThread;
34
0
    nsresult rv = NS_NewNamedThread("AutoTestThread", getter_AddRefs(newThread));
35
0
    if (NS_FAILED(rv))
36
0
      return;
37
0
38
0
    newThread.swap(mThread);
39
0
  }
40
41
0
  ~AutoTestThread() {
42
0
    mThread->Shutdown();
43
0
  }
44
45
0
  operator nsIThread*() const {
46
0
    return mThread;
47
0
  }
48
49
0
  nsIThread* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
50
0
    return mThread;
51
0
  }
52
53
private:
54
  nsCOMPtr<nsIThread> mThread;
55
};
56
57
class AutoCreateAndDestroyReentrantMonitor
58
{
59
public:
60
0
  AutoCreateAndDestroyReentrantMonitor() {
61
0
    mReentrantMonitor = new ReentrantMonitor("TestTimers::AutoMon");
62
0
    MOZ_RELEASE_ASSERT(mReentrantMonitor, "Out of memory!");
63
0
  }
64
65
0
  ~AutoCreateAndDestroyReentrantMonitor() {
66
0
    delete mReentrantMonitor;
67
0
  }
68
69
0
  operator ReentrantMonitor* () const {
70
0
    return mReentrantMonitor;
71
0
  }
72
73
private:
74
  ReentrantMonitor* mReentrantMonitor;
75
};
76
77
class TimerCallback final : public nsITimerCallback
78
{
79
public:
80
  NS_DECL_THREADSAFE_ISUPPORTS
81
82
  TimerCallback(nsIThread** aThreadPtr, ReentrantMonitor* aReentrantMonitor)
83
0
  : mThreadPtr(aThreadPtr), mReentrantMonitor(aReentrantMonitor) { }
84
85
0
  NS_IMETHOD Notify(nsITimer* aTimer) override {
86
0
    MOZ_RELEASE_ASSERT(mThreadPtr, "Callback was not supposed to be called!");
87
0
    nsCOMPtr<nsIThread> current(do_GetCurrentThread());
88
0
89
0
    ReentrantMonitorAutoEnter mon(*mReentrantMonitor);
90
0
91
0
    MOZ_RELEASE_ASSERT(!*mThreadPtr, "Timer called back more than once!");
92
0
    *mThreadPtr = current;
93
0
94
0
    mon.Notify();
95
0
96
0
    return NS_OK;
97
0
  }
98
private:
99
0
  ~TimerCallback() {}
100
101
  nsIThread** mThreadPtr;
102
  ReentrantMonitor* mReentrantMonitor;
103
};
104
105
NS_IMPL_ISUPPORTS(TimerCallback, nsITimerCallback)
106
107
TEST(Timers, TargetedTimers)
108
0
{
109
0
  AutoCreateAndDestroyReentrantMonitor newMon;
110
0
  ASSERT_TRUE(newMon);
111
0
112
0
  AutoTestThread testThread;
113
0
  ASSERT_TRUE(testThread);
114
0
115
0
  nsIThread* notifiedThread = nullptr;
116
0
117
0
  nsCOMPtr<nsITimerCallback> callback =
118
0
    new TimerCallback(&notifiedThread, newMon);
119
0
  ASSERT_TRUE(callback);
120
0
121
0
  nsIEventTarget* target = static_cast<nsIEventTarget*>(testThread);
122
0
123
0
  nsCOMPtr<nsITimer> timer;
124
0
  nsresult rv = NS_NewTimerWithCallback(getter_AddRefs(timer),
125
0
                                        callback, 2000, nsITimer::TYPE_ONE_SHOT,
126
0
                                        target);
127
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
128
0
129
0
  ReentrantMonitorAutoEnter mon(*newMon);
130
0
  while (!notifiedThread) {
131
0
    mon.Wait();
132
0
  }
133
0
  ASSERT_EQ(notifiedThread, testThread);
134
0
}
135
136
TEST(Timers, TimerWithStoppedTarget)
137
0
{
138
0
  AutoTestThread testThread;
139
0
  ASSERT_TRUE(testThread);
140
0
141
0
  nsIEventTarget* target = static_cast<nsIEventTarget*>(testThread);
142
0
143
0
  // If this is called, we'll assert
144
0
  nsCOMPtr<nsITimerCallback> callback =
145
0
    new TimerCallback(nullptr, nullptr);
146
0
  ASSERT_TRUE(callback);
147
0
148
0
  nsCOMPtr<nsITimer> timer;
149
0
  nsresult rv = NS_NewTimerWithCallback(getter_AddRefs(timer),
150
0
                                        callback, 100, nsITimer::TYPE_ONE_SHOT,
151
0
                                        target);
152
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
153
0
154
0
  testThread->Shutdown();
155
0
156
0
  PR_Sleep(400);
157
0
}
158
159
// gtest on 32bit Win7 debug build is unstable and somehow this test
160
// makes it even worse.
161
#if !defined(XP_WIN) || !defined(DEBUG) || defined(HAVE_64BIT_BUILD)
162
163
class FindExpirationTimeState final
164
{
165
public:
166
  // We'll offset the timers 10 seconds into the future to assure that they won't fire
167
  const uint32_t kTimerOffset = 10 * 1000;
168
  // And we'll set the timers spaced by 5 seconds.
169
  const uint32_t kTimerInterval = 5 * 1000;
170
  // We'll use 20 timers
171
  const uint32_t kNumTimers = 20;
172
173
  TimeStamp mBefore;
174
  TimeStamp mMiddle;
175
176
  std::list<nsCOMPtr<nsITimer>> mTimers;
177
178
  ~FindExpirationTimeState()
179
0
  {
180
0
    while (!mTimers.empty()) {
181
0
      nsCOMPtr<nsITimer> t = mTimers.front().get();
182
0
      mTimers.pop_front();
183
0
      t->Cancel();
184
0
    }
185
0
  }
186
187
  // Create timers, with aNumLowPriority low priority timers first in the queue
188
  void InitTimers(uint32_t aNumLowPriority, uint32_t aType)
189
0
  {
190
0
    // aType is just for readability.
191
0
    MOZ_ASSERT(aType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
192
0
    InitTimers(aNumLowPriority, nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, nullptr);
193
0
  }
194
195
  // Create timers, with aNumDifferentTarget timers with target aTarget first in the queue
196
  void InitTimers(uint32_t aNumDifferentTarget, nsIEventTarget* aTarget)
197
0
  {
198
0
    InitTimers(aNumDifferentTarget, nsITimer::TYPE_ONE_SHOT, aTarget);
199
0
  }
200
201
  void InitTimers(uint32_t aNumDifferingTimers, uint32_t aType, nsIEventTarget* aTarget)
202
0
  {
203
0
    do {
204
0
      TimeStamp clearUntil =
205
0
        TimeStamp::Now() +
206
0
        TimeDuration::FromMilliseconds(kTimerOffset +
207
0
                                       kNumTimers * kTimerInterval);
208
0
209
0
      // NS_GetTimerDeadlineHintOnCurrentThread returns clearUntil if there are
210
0
      // no pending timers before clearUntil.
211
0
      TimeStamp t = NS_GetTimerDeadlineHintOnCurrentThread(clearUntil, 100);
212
0
      if (t >= clearUntil) {
213
0
        break;
214
0
      }
215
0
216
0
      // Clear whatever random timers there might be pending.
217
0
      uint32_t waitTime = 10;
218
0
      if (t > TimeStamp::Now()) {
219
0
        waitTime = uint32_t((t - TimeStamp::Now()).ToMilliseconds());
220
0
      }
221
0
      PR_Sleep(PR_MillisecondsToInterval(waitTime));
222
0
    } while(true);
223
0
224
0
    mBefore = TimeStamp::Now();
225
0
    mMiddle = mBefore + TimeDuration::FromMilliseconds(
226
0
        kTimerOffset + kTimerInterval * kNumTimers / 2);
227
0
    for (uint32_t i = 0; i < kNumTimers; ++i) {
228
0
      nsCOMPtr<nsITimer> timer = NS_NewTimer();
229
0
      ASSERT_TRUE(timer);
230
0
231
0
      if (i < aNumDifferingTimers) {
232
0
        if (aTarget) {
233
0
          timer->SetTarget(aTarget);
234
0
        }
235
0
236
0
        timer->InitWithNamedFuncCallback(&UnusedCallbackFunc,
237
0
                                         nullptr,
238
0
                                         kTimerOffset + kTimerInterval * i,
239
0
                                         aType,
240
0
                                         "FindExpirationTimeState::InitTimers");
241
0
      } else {
242
0
        timer->InitWithNamedFuncCallback(&UnusedCallbackFunc,
243
0
                                         nullptr,
244
0
                                         kTimerOffset + kTimerInterval * i,
245
0
                                         nsITimer::TYPE_ONE_SHOT,
246
0
                                         "FindExpirationTimeState::InitTimers");
247
0
      }
248
0
      mTimers.push_front(timer.get());
249
0
    }
250
0
  }
251
252
  static void UnusedCallbackFunc(nsITimer* aTimer, void* aClosure)
253
0
  {
254
0
    FAIL() << "Timer shouldn't fire.";
255
0
  }
256
};
257
258
TEST(Timers, FindExpirationTime)
259
0
{
260
0
  {
261
0
    FindExpirationTimeState state;
262
0
    // 0 low priority timers
263
0
    state.InitTimers(0, nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
264
0
    TimeStamp before = state.mBefore;
265
0
    TimeStamp middle = state.mMiddle;
266
0
267
0
    TimeStamp t;
268
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 0);
269
0
    EXPECT_TRUE(t) << "We should find a time";
270
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
271
0
272
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 20);
273
0
    EXPECT_TRUE(t) << "We should find a time";
274
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
275
0
276
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 0);
277
0
    EXPECT_TRUE(t) << "We should find a time";
278
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
279
0
280
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 10);
281
0
    EXPECT_TRUE(t) << "We should find a time";
282
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
283
0
284
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 20);
285
0
    EXPECT_TRUE(t) << "We should find a time";
286
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
287
0
  }
288
0
289
0
  {
290
0
    FindExpirationTimeState state;
291
0
    // 5 low priority timers
292
0
    state.InitTimers(5, nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
293
0
    TimeStamp before = state.mBefore;
294
0
    TimeStamp middle = state.mMiddle;
295
0
296
0
    TimeStamp t;
297
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 0);
298
0
    EXPECT_TRUE(t) << "We should find a time";
299
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
300
0
301
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 20);
302
0
    EXPECT_TRUE(t) << "We should find a time";
303
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
304
0
305
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 0);
306
0
    EXPECT_TRUE(t) << "We should find a time";
307
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
308
0
309
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 10);
310
0
    EXPECT_TRUE(t) << "We should find a time";
311
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
312
0
313
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 20);
314
0
    EXPECT_TRUE(t) << "We should find a time";
315
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
316
0
  }
317
0
318
0
  {
319
0
    FindExpirationTimeState state;
320
0
    // 15 low priority timers
321
0
    state.InitTimers(15, nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
322
0
    TimeStamp before = state.mBefore;
323
0
    TimeStamp middle = state.mMiddle;
324
0
325
0
    TimeStamp t;
326
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 0);
327
0
    EXPECT_TRUE(t) << "We should find a time";
328
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
329
0
330
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 20);
331
0
    EXPECT_TRUE(t) << "We should find a time";
332
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
333
0
334
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 0);
335
0
    EXPECT_TRUE(t) << "We should find a time";
336
0
    EXPECT_LT(t, middle) << "Found time should be equal to default";
337
0
338
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 10);
339
0
    EXPECT_TRUE(t) << "We should find a time";
340
0
    EXPECT_EQ(t, middle) << "Found time should be equal to default";
341
0
342
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 20);
343
0
    EXPECT_TRUE(t) << "We should find a time";
344
0
    EXPECT_EQ(t, middle) << "Found time should be equal to default";
345
0
  }
346
0
347
0
  {
348
0
    AutoTestThread testThread;
349
0
    FindExpirationTimeState state;
350
0
    // 5 other targets
351
0
    state.InitTimers(5, static_cast<nsIEventTarget*>(testThread));
352
0
    TimeStamp before = state.mBefore;
353
0
    TimeStamp middle = state.mMiddle;
354
0
355
0
    TimeStamp t;
356
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 0);
357
0
    EXPECT_TRUE(t) << "We should find a time";
358
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
359
0
360
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 20);
361
0
    EXPECT_TRUE(t) << "We should find a time";
362
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
363
0
364
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 0);
365
0
    EXPECT_TRUE(t) << "We should find a time";
366
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
367
0
368
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 10);
369
0
    EXPECT_TRUE(t) << "We should find a time";
370
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
371
0
372
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 20);
373
0
    EXPECT_TRUE(t) << "We should find a time";
374
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
375
0
  }
376
0
377
0
  {
378
0
    AutoTestThread testThread;
379
0
    FindExpirationTimeState state;
380
0
    // 15 other targets
381
0
    state.InitTimers(15, static_cast<nsIEventTarget*>(testThread));
382
0
    TimeStamp before = state.mBefore;
383
0
    TimeStamp middle = state.mMiddle;
384
0
385
0
    TimeStamp t;
386
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 0);
387
0
    EXPECT_TRUE(t) << "We should find a time";
388
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
389
0
390
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(before, 20);
391
0
    EXPECT_TRUE(t) << "We should find a time";
392
0
    EXPECT_EQ(t, before) << "Found time should be equal to default";
393
0
394
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 0);
395
0
    EXPECT_TRUE(t) << "We should find a time";
396
0
    EXPECT_LT(t, middle) << "Found time should be less than default";
397
0
398
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 10);
399
0
    EXPECT_TRUE(t) << "We should find a time";
400
0
    EXPECT_EQ(t, middle) << "Found time should be equal to default";
401
0
402
0
    t = NS_GetTimerDeadlineHintOnCurrentThread(middle, 20);
403
0
    EXPECT_TRUE(t) << "We should find a time";
404
0
    EXPECT_EQ(t, middle) << "Found time should be equal to default";
405
0
  }
406
0
}
407
408
#endif
409
410
0
#define FUZZ_MAX_TIMEOUT 9
411
class FuzzTestThreadState final : public nsITimerCallback {
412
  public:
413
    NS_DECL_THREADSAFE_ISUPPORTS
414
415
    explicit FuzzTestThreadState(nsIThread* thread) :
416
      mThread(thread),
417
      mStopped(false)
418
0
    {}
419
420
    class StartRunnable final : public mozilla::Runnable {
421
      public:
422
        explicit StartRunnable(FuzzTestThreadState* threadState)
423
          : mozilla::Runnable("FuzzTestThreadState::StartRunnable")
424
          , mThreadState(threadState)
425
0
        {}
426
427
        NS_IMETHOD Run() override
428
0
        {
429
0
          mThreadState->ScheduleOrCancelTimers();
430
0
          return NS_OK;
431
0
        }
432
433
      private:
434
        RefPtr<FuzzTestThreadState> mThreadState;
435
    };
436
437
    void Start()
438
0
    {
439
0
      nsCOMPtr<nsIRunnable> runnable = new StartRunnable(this);
440
0
      nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
441
0
      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch StartRunnable.");
442
0
    }
443
444
    void Stop()
445
0
    {
446
0
      mStopped = true;
447
0
    }
448
449
    NS_IMETHOD Notify(nsITimer* aTimer) override
450
0
    {
451
0
      bool onCorrectThread;
452
0
      nsresult rv = mThread->IsOnCurrentThread(&onCorrectThread);
453
0
      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to perform thread check.");
454
0
      MOZ_RELEASE_ASSERT(onCorrectThread, "Notify invoked on wrong thread.");
455
0
456
0
      uint32_t delay;
457
0
      rv = aTimer->GetDelay(&delay);
458
0
      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "GetDelay failed.");
459
0
460
0
      MOZ_RELEASE_ASSERT(delay <= FUZZ_MAX_TIMEOUT,
461
0
                         "Delay was an invalid value for this test.");
462
0
463
0
      uint32_t type;
464
0
      rv = aTimer->GetType(&type);
465
0
      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to get timer type.");
466
0
      MOZ_RELEASE_ASSERT(type <= nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
467
0
468
0
      if (type == nsITimer::TYPE_ONE_SHOT) {
469
0
        MOZ_RELEASE_ASSERT(!mOneShotTimersByDelay[delay].empty(),
470
0
                           "Unexpected one-shot timer.");
471
0
472
0
        MOZ_RELEASE_ASSERT(mOneShotTimersByDelay[delay].front().get() == aTimer,
473
0
                           "One-shot timers have been reordered.");
474
0
475
0
        mOneShotTimersByDelay[delay].pop_front();
476
0
        --mTimersOutstanding;
477
0
      } else if (mStopped) {
478
0
        CancelRepeatingTimer(aTimer);
479
0
      }
480
0
481
0
      ScheduleOrCancelTimers();
482
0
      RescheduleSomeTimers();
483
0
      return NS_OK;
484
0
    }
485
486
    bool HasTimersOutstanding() const
487
0
    {
488
0
      return !!mTimersOutstanding;
489
0
    }
490
491
  private:
492
    ~FuzzTestThreadState()
493
0
    {
494
0
      for (size_t i = 0; i <= FUZZ_MAX_TIMEOUT; ++i) {
495
0
        MOZ_RELEASE_ASSERT(mOneShotTimersByDelay[i].empty(),
496
0
                           "Timers remain at end of test.");
497
0
      }
498
0
    }
499
500
    uint32_t GetRandomType() const
501
0
    {
502
0
      return rand() % (nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP + 1);
503
0
    }
504
505
    size_t CountOneShotTimers() const
506
0
    {
507
0
      size_t count = 0;
508
0
      for (size_t i = 0; i <= FUZZ_MAX_TIMEOUT; ++i) {
509
0
        count += mOneShotTimersByDelay[i].size();
510
0
      }
511
0
      return count;
512
0
    }
513
514
    void ScheduleOrCancelTimers()
515
0
    {
516
0
      if (mStopped) {
517
0
        return;
518
0
      }
519
0
520
0
      const size_t numTimersDesired = (rand() % 100) + 100;
521
0
      MOZ_RELEASE_ASSERT(numTimersDesired >= 100);
522
0
      MOZ_RELEASE_ASSERT(numTimersDesired < 200);
523
0
      int adjustment = numTimersDesired - mTimersOutstanding;
524
0
525
0
      while (adjustment > 0) {
526
0
        CreateRandomTimer();
527
0
        --adjustment;
528
0
      }
529
0
530
0
      while (adjustment < 0) {
531
0
        CancelRandomTimer();
532
0
        ++adjustment;
533
0
      }
534
0
535
0
      MOZ_RELEASE_ASSERT(numTimersDesired == mTimersOutstanding);
536
0
    }
537
538
    void RescheduleSomeTimers()
539
0
    {
540
0
      if (mStopped) {
541
0
        return;
542
0
      }
543
0
544
0
      static const size_t kNumRescheduled = 40;
545
0
546
0
      // Reschedule some timers with a Cancel first.
547
0
      for (size_t i = 0; i < kNumRescheduled; ++i) {
548
0
        InitRandomTimer(CancelRandomTimer().get());
549
0
      }
550
0
      // Reschedule some timers without a Cancel first.
551
0
      for (size_t i = 0; i < kNumRescheduled; ++i) {
552
0
        InitRandomTimer(RemoveRandomTimer().get());
553
0
      }
554
0
    }
555
556
    void CreateRandomTimer()
557
0
    {
558
0
      nsCOMPtr<nsITimer> timer = NS_NewTimer(static_cast<nsIEventTarget*>(mThread.get()));
559
0
      MOZ_RELEASE_ASSERT(timer, "Failed to create timer.");
560
0
561
0
      InitRandomTimer(timer.get());
562
0
    }
563
564
    nsCOMPtr<nsITimer> CancelRandomTimer()
565
0
    {
566
0
      nsCOMPtr<nsITimer> timer(RemoveRandomTimer());
567
0
      timer->Cancel();
568
0
      return timer;
569
0
    }
570
571
    nsCOMPtr<nsITimer> RemoveRandomTimer()
572
0
    {
573
0
      MOZ_RELEASE_ASSERT(mTimersOutstanding);
574
0
575
0
      if ((GetRandomType() == nsITimer::TYPE_ONE_SHOT && CountOneShotTimers())
576
0
          || mRepeatingTimers.empty()) {
577
0
        uint32_t delayToRemove = rand() % (FUZZ_MAX_TIMEOUT + 1);
578
0
        while (mOneShotTimersByDelay[delayToRemove].empty()) {
579
0
          // ++delayToRemove mod FUZZ_MAX_TIMEOUT + 1
580
0
          delayToRemove = (delayToRemove + 1) % (FUZZ_MAX_TIMEOUT + 1);
581
0
        }
582
0
583
0
        uint32_t indexToRemove =
584
0
          rand() % mOneShotTimersByDelay[delayToRemove].size();
585
0
586
0
        for (auto it = mOneShotTimersByDelay[delayToRemove].begin();
587
0
             it != mOneShotTimersByDelay[delayToRemove].end();
588
0
             ++it) {
589
0
          if (indexToRemove) {
590
0
            --indexToRemove;
591
0
            continue;
592
0
          }
593
0
594
0
          nsCOMPtr<nsITimer> removed = *it;
595
0
          mOneShotTimersByDelay[delayToRemove].erase(it);
596
0
          --mTimersOutstanding;
597
0
          return removed;
598
0
        }
599
0
      } else {
600
0
        size_t indexToRemove = rand() % mRepeatingTimers.size();
601
0
        nsCOMPtr<nsITimer> removed(mRepeatingTimers[indexToRemove]);
602
0
        mRepeatingTimers.erase(mRepeatingTimers.begin() + indexToRemove);
603
0
        --mTimersOutstanding;
604
0
        return removed;
605
0
      }
606
0
607
0
      MOZ_CRASH("Unable to remove a timer");
608
0
    }
609
610
    void InitRandomTimer(nsITimer* aTimer)
611
0
    {
612
0
      // Between 0 and FUZZ_MAX_TIMEOUT
613
0
      uint32_t delay = rand() % (FUZZ_MAX_TIMEOUT + 1);
614
0
      uint32_t type = GetRandomType();
615
0
      nsresult rv = aTimer->InitWithCallback(this, delay, type);
616
0
      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to set timer.");
617
0
618
0
      if (type == nsITimer::TYPE_ONE_SHOT) {
619
0
        mOneShotTimersByDelay[delay].push_back(aTimer);
620
0
      } else {
621
0
        mRepeatingTimers.push_back(aTimer);
622
0
      }
623
0
      ++mTimersOutstanding;
624
0
    }
625
626
    void CancelRepeatingTimer(nsITimer* aTimer)
627
0
    {
628
0
      for (auto it = mRepeatingTimers.begin();
629
0
           it != mRepeatingTimers.end();
630
0
           ++it) {
631
0
        if (it->get() == aTimer) {
632
0
          mRepeatingTimers.erase(it);
633
0
          aTimer->Cancel();
634
0
          --mTimersOutstanding;
635
0
          return;
636
0
        }
637
0
      }
638
0
    }
639
640
    nsCOMPtr<nsIThread> mThread;
641
    // Scheduled timers, indexed by delay between 0-9 ms, in lists
642
    // with most recently scheduled last.
643
    std::list<nsCOMPtr<nsITimer>> mOneShotTimersByDelay[FUZZ_MAX_TIMEOUT + 1];
644
    std::vector<nsCOMPtr<nsITimer>> mRepeatingTimers;
645
    Atomic<bool> mStopped;
646
    Atomic<size_t> mTimersOutstanding;
647
};
648
649
NS_IMPL_ISUPPORTS(FuzzTestThreadState, nsITimerCallback)
650
651
TEST(Timers, FuzzTestTimers)
652
0
{
653
0
  static const size_t kNumThreads(10);
654
0
  AutoTestThread threads[kNumThreads];
655
0
  RefPtr<FuzzTestThreadState> threadStates[kNumThreads];
656
0
657
0
  for (size_t i = 0; i < kNumThreads; ++i) {
658
0
    threadStates[i] = new FuzzTestThreadState(&*threads[i]);
659
0
    threadStates[i]->Start();
660
0
  }
661
0
662
0
  PR_Sleep(PR_MillisecondsToInterval(20000));
663
0
664
0
  for (size_t i = 0; i < kNumThreads; ++i) {
665
0
    threadStates[i]->Stop();
666
0
  }
667
0
668
0
  // Wait at most 10 seconds for all outstanding timers to pop
669
0
  PRIntervalTime start = PR_IntervalNow();
670
0
  for (auto& threadState : threadStates) {
671
0
    while (threadState->HasTimersOutstanding()) {
672
0
      uint32_t elapsedMs = PR_IntervalToMilliseconds(PR_IntervalNow() - start);
673
0
      ASSERT_LE(elapsedMs, uint32_t(10000)) << "Timed out waiting for all timers to pop";
674
0
      PR_Sleep(PR_MillisecondsToInterval(10));
675
0
    }
676
0
  }
677
0
}