Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/tests/gtest/TestThreadUtils.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http:mozilla.org/MPL/2.0/. */
4
5
#include "nsComponentManagerUtils.h"
6
#include "nsThreadUtils.h"
7
#include "mozilla/IdleTaskRunner.h"
8
#include "mozilla/UniquePtr.h"
9
10
#include "gtest/gtest.h"
11
12
using namespace mozilla;
13
14
enum {
15
  TEST_CALL_VOID_ARG_VOID_RETURN,
16
  TEST_CALL_VOID_ARG_VOID_RETURN_CONST,
17
  TEST_CALL_VOID_ARG_NONVOID_RETURN,
18
  TEST_CALL_NONVOID_ARG_VOID_RETURN,
19
  TEST_CALL_NONVOID_ARG_NONVOID_RETURN,
20
  TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT,
21
  TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT,
22
#ifdef HAVE_STDCALL
23
  TEST_STDCALL_VOID_ARG_VOID_RETURN,
24
  TEST_STDCALL_VOID_ARG_NONVOID_RETURN,
25
  TEST_STDCALL_NONVOID_ARG_VOID_RETURN,
26
  TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN,
27
  TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT,
28
#endif
29
  TEST_CALL_NEWTHREAD_SUICIDAL,
30
  MAX_TESTS
31
};
32
33
bool gRunnableExecuted[MAX_TESTS];
34
35
class nsFoo : public nsISupports {
36
  NS_DECL_ISUPPORTS
37
0
  nsresult DoFoo(bool* aBool) {
38
0
    *aBool = true;
39
0
    return NS_OK;
40
0
  }
41
42
private:
43
0
  virtual ~nsFoo() {}
44
};
45
46
NS_IMPL_ISUPPORTS0(nsFoo)
47
48
class TestSuicide : public mozilla::Runnable {
49
public:
50
0
  TestSuicide() : mozilla::Runnable("TestSuicide") {}
51
0
  NS_IMETHOD Run() override {
52
0
    // Runs first time on thread "Suicide", then dies on MainThread
53
0
    if (!NS_IsMainThread()) {
54
0
      mThread = do_GetCurrentThread();
55
0
      NS_DispatchToMainThread(this);
56
0
      return NS_OK;
57
0
    }
58
0
    MOZ_RELEASE_ASSERT(mThread);
59
0
    mThread->Shutdown();
60
0
    gRunnableExecuted[TEST_CALL_NEWTHREAD_SUICIDAL] = true;
61
0
    return NS_OK;
62
0
  }
63
64
private:
65
  nsCOMPtr<nsIThread> mThread;
66
};
67
68
class nsBar : public nsISupports {
69
0
  virtual ~nsBar() {}
70
public:
71
  NS_DECL_ISUPPORTS
72
0
  void DoBar1(void) {
73
0
    gRunnableExecuted[TEST_CALL_VOID_ARG_VOID_RETURN] = true;
74
0
  }
75
0
  void DoBar1Const(void) const {
76
0
    gRunnableExecuted[TEST_CALL_VOID_ARG_VOID_RETURN_CONST] = true;
77
0
  }
78
0
  nsresult DoBar2(void) {
79
0
    gRunnableExecuted[TEST_CALL_VOID_ARG_NONVOID_RETURN] = true;
80
0
    return NS_OK;
81
0
  }
82
0
  void DoBar3(nsFoo* aFoo) {
83
0
    aFoo->DoFoo(&gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN]);
84
0
  }
85
0
  nsresult DoBar4(nsFoo* aFoo) {
86
0
    return aFoo->DoFoo(&gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN]);
87
0
  }
88
0
  void DoBar5(nsFoo* aFoo) {
89
0
    if (aFoo)
90
0
      gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
91
0
  }
92
0
  nsresult DoBar6(char* aFoo) {
93
0
    if (strlen(aFoo))
94
0
      gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT] = true;
95
0
    return NS_OK;
96
0
  }
97
#ifdef HAVE_STDCALL
98
  void __stdcall DoBar1std(void) {
99
    gRunnableExecuted[TEST_STDCALL_VOID_ARG_VOID_RETURN] = true;
100
  }
101
  nsresult __stdcall DoBar2std(void) {
102
    gRunnableExecuted[TEST_STDCALL_VOID_ARG_NONVOID_RETURN] = true;
103
    return NS_OK;
104
  }
105
  void __stdcall DoBar3std(nsFoo* aFoo) {
106
    aFoo->DoFoo(&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN]);
107
  }
108
  nsresult __stdcall DoBar4std(nsFoo* aFoo) {
109
    return aFoo->DoFoo(&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN]);
110
  }
111
  void __stdcall DoBar5std(nsFoo* aFoo) {
112
    if (aFoo)
113
      gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
114
  }
115
  nsresult __stdcall DoBar6std(char* aFoo) {
116
    if (strlen(aFoo))
117
      gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
118
    return NS_OK;
119
  }
120
#endif
121
};
122
123
NS_IMPL_ISUPPORTS0(nsBar)
124
125
struct TestCopyWithNoMove
126
{
127
0
  explicit TestCopyWithNoMove(int* aCopyCounter) : mCopyCounter(aCopyCounter) {}
128
0
  TestCopyWithNoMove(const TestCopyWithNoMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; };
129
  // No 'move' declaration, allows passing object by rvalue copy.
130
  // Destructor nulls member variable...
131
0
  ~TestCopyWithNoMove() { mCopyCounter = nullptr; }
132
  // ... so we can check that the object is called when still alive.
133
0
  void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); }
134
  int* mCopyCounter;
135
};
136
struct TestCopyWithDeletedMove
137
{
138
0
  explicit TestCopyWithDeletedMove(int* aCopyCounter) : mCopyCounter(aCopyCounter) {}
139
0
  TestCopyWithDeletedMove(const TestCopyWithDeletedMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; };
140
  // Deleted move prevents passing by rvalue (even if copy would work)
141
  TestCopyWithDeletedMove(TestCopyWithDeletedMove&&) = delete;
142
0
  ~TestCopyWithDeletedMove() { mCopyCounter = nullptr; }
143
0
  void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); }
144
  int* mCopyCounter;
145
};
146
struct TestMove
147
{
148
0
  explicit TestMove(int* aMoveCounter) : mMoveCounter(aMoveCounter) {}
149
  TestMove(const TestMove&) = delete;
150
0
  TestMove(TestMove&& a) : mMoveCounter(a.mMoveCounter) { a.mMoveCounter = nullptr; ++mMoveCounter; }
151
0
  ~TestMove() { mMoveCounter = nullptr; }
152
0
  void operator()() { MOZ_RELEASE_ASSERT(mMoveCounter); }
153
  int* mMoveCounter;
154
};
155
struct TestCopyMove
156
{
157
0
  TestCopyMove(int* aCopyCounter, int* aMoveCounter) : mCopyCounter(aCopyCounter), mMoveCounter(aMoveCounter) {}
158
0
  TestCopyMove(const TestCopyMove& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { ++mCopyCounter; };
159
0
  TestCopyMove(TestCopyMove&& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { a.mMoveCounter = nullptr; ++mMoveCounter; }
160
0
  ~TestCopyMove() { mCopyCounter = nullptr; mMoveCounter = nullptr; }
161
0
  void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); MOZ_RELEASE_ASSERT(mMoveCounter); }
162
  int* mCopyCounter;
163
  int* mMoveCounter;
164
};
165
166
static void Expect(const char* aContext, int aCounter, int aMaxExpected)
167
0
{
168
0
  EXPECT_LE(aCounter, aMaxExpected) << aContext;
169
0
}
170
171
static void ExpectRunnableName(Runnable* aRunnable, const char* aExpectedName)
172
0
{
173
0
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
174
0
  nsAutoCString name;
175
0
  EXPECT_TRUE(NS_SUCCEEDED(aRunnable->GetName(name))) << "Runnable::GetName()";
176
0
  EXPECT_TRUE(name.EqualsASCII(aExpectedName)) << "Verify Runnable name";
177
0
#endif
178
0
}
179
180
static void TestNewRunnableFunction(bool aNamed)
181
0
{
182
0
  // Test NS_NewRunnableFunction with copyable-only function object.
183
0
  {
184
0
    int copyCounter = 0;
185
0
    {
186
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
187
0
      {
188
0
        TestCopyWithNoMove tracker(&copyCounter);
189
0
        trackedRunnable =
190
0
          aNamed ? NS_NewRunnableFunction("unused", tracker)
191
0
                 : NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
192
0
        // Original 'tracker' is destroyed here.
193
0
      }
194
0
      // Verify that the runnable contains a non-destroyed function object.
195
0
      trackedRunnable->Run();
196
0
    }
197
0
    Expect("NS_NewRunnableFunction with copyable-only (and no move) function, copies",
198
0
           copyCounter, 1);
199
0
  }
200
0
  {
201
0
    int copyCounter = 0;
202
0
    {
203
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
204
0
      {
205
0
        // Passing as rvalue, but using copy.
206
0
        // (TestCopyWithDeletedMove wouldn't allow this.)
207
0
        trackedRunnable =
208
0
          aNamed
209
0
            ? NS_NewRunnableFunction("unused", TestCopyWithNoMove(&copyCounter))
210
0
            : NS_NewRunnableFunction("TestNewRunnableFunction",
211
0
                                     TestCopyWithNoMove(&copyCounter));
212
0
      }
213
0
      trackedRunnable->Run();
214
0
    }
215
0
    Expect("NS_NewRunnableFunction with copyable-only (and no move) function rvalue, copies",
216
0
           copyCounter, 1);
217
0
  }
218
0
  {
219
0
    int copyCounter = 0;
220
0
    {
221
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
222
0
      {
223
0
        TestCopyWithDeletedMove tracker(&copyCounter);
224
0
        trackedRunnable =
225
0
          aNamed ? NS_NewRunnableFunction("unused", tracker)
226
0
                 : NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
227
0
      }
228
0
      trackedRunnable->Run();
229
0
    }
230
0
    Expect("NS_NewRunnableFunction with copyable-only (and deleted move) function, copies",
231
0
           copyCounter, 1);
232
0
  }
233
0
234
0
  // Test NS_NewRunnableFunction with movable-only function object.
235
0
  {
236
0
    int moveCounter = 0;
237
0
    {
238
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
239
0
      {
240
0
        TestMove tracker(&moveCounter);
241
0
        trackedRunnable =
242
0
          aNamed
243
0
            ? NS_NewRunnableFunction("unused", std::move(tracker))
244
0
            : NS_NewRunnableFunction("TestNewRunnableFunction", std::move(tracker));
245
0
      }
246
0
      trackedRunnable->Run();
247
0
    }
248
0
    Expect("NS_NewRunnableFunction with movable-only function, moves",
249
0
           moveCounter, 1);
250
0
  }
251
0
  {
252
0
    int moveCounter = 0;
253
0
    {
254
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
255
0
      {
256
0
        trackedRunnable =
257
0
          aNamed ? NS_NewRunnableFunction("unused", TestMove(&moveCounter))
258
0
                 : NS_NewRunnableFunction("TestNewRunnableFunction",
259
0
                                          TestMove(&moveCounter));
260
0
      }
261
0
      trackedRunnable->Run();
262
0
    }
263
0
    Expect("NS_NewRunnableFunction with movable-only function rvalue, moves",
264
0
           moveCounter, 1);
265
0
  }
266
0
267
0
  // Test NS_NewRunnableFunction with copyable&movable function object.
268
0
  {
269
0
    int copyCounter = 0;
270
0
    int moveCounter = 0;
271
0
    {
272
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
273
0
      {
274
0
        TestCopyMove tracker(&copyCounter, &moveCounter);
275
0
        trackedRunnable =
276
0
          aNamed
277
0
            ? NS_NewRunnableFunction("unused", std::move(tracker))
278
0
            : NS_NewRunnableFunction("TestNewRunnableFunction", std::move(tracker));
279
0
      }
280
0
      trackedRunnable->Run();
281
0
    }
282
0
    Expect("NS_NewRunnableFunction with copyable&movable function, copies",
283
0
           copyCounter, 0);
284
0
    Expect("NS_NewRunnableFunction with copyable&movable function, moves",
285
0
           moveCounter, 1);
286
0
  }
287
0
  {
288
0
    int copyCounter = 0;
289
0
    int moveCounter = 0;
290
0
    {
291
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
292
0
      {
293
0
        trackedRunnable =
294
0
          aNamed
295
0
            ? NS_NewRunnableFunction("unused",
296
0
                                     TestCopyMove(&copyCounter, &moveCounter))
297
0
            : NS_NewRunnableFunction("TestNewRunnableFunction",
298
0
                                     TestCopyMove(&copyCounter, &moveCounter));
299
0
      }
300
0
      trackedRunnable->Run();
301
0
    }
302
0
    Expect("NS_NewRunnableFunction with copyable&movable function rvalue, copies",
303
0
           copyCounter, 0);
304
0
    Expect("NS_NewRunnableFunction with copyable&movable function rvalue, moves",
305
0
           moveCounter, 1);
306
0
  }
307
0
308
0
  // Test NS_NewRunnableFunction with copyable-only lambda capture.
309
0
  {
310
0
    int copyCounter = 0;
311
0
    {
312
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
313
0
      {
314
0
        TestCopyWithNoMove tracker(&copyCounter);
315
0
        // Expect 2 copies (here -> local lambda -> runnable lambda).
316
0
        trackedRunnable =
317
0
          aNamed ? NS_NewRunnableFunction("unused",
318
0
                                          [tracker]() mutable { tracker(); })
319
0
                 : NS_NewRunnableFunction("TestNewRunnableFunction",
320
0
                                          [tracker]() mutable { tracker(); });
321
0
      }
322
0
      trackedRunnable->Run();
323
0
    }
324
0
    Expect("NS_NewRunnableFunction with copyable-only (and no move) capture, copies",
325
0
           copyCounter, 2);
326
0
  }
327
0
  {
328
0
    int copyCounter = 0;
329
0
    {
330
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
331
0
      {
332
0
        TestCopyWithDeletedMove tracker(&copyCounter);
333
0
        // Expect 2 copies (here -> local lambda -> runnable lambda).
334
0
        trackedRunnable =
335
0
          aNamed ? NS_NewRunnableFunction("unused",
336
0
                                          [tracker]() mutable { tracker(); })
337
0
                 : NS_NewRunnableFunction("TestNewRunnableFunction",
338
0
                                          [tracker]() mutable { tracker(); });
339
0
      }
340
0
      trackedRunnable->Run();
341
0
    }
342
0
    Expect("NS_NewRunnableFunction with copyable-only (and deleted move) capture, copies",
343
0
           copyCounter, 2);
344
0
  }
345
0
346
0
  // Note: Not possible to use move-only captures.
347
0
  // (Until we can use C++14 generalized lambda captures)
348
0
349
0
  // Test NS_NewRunnableFunction with copyable&movable lambda capture.
350
0
  {
351
0
    int copyCounter = 0;
352
0
    int moveCounter = 0;
353
0
    {
354
0
      nsCOMPtr<nsIRunnable> trackedRunnable;
355
0
      {
356
0
        TestCopyMove tracker(&copyCounter, &moveCounter);
357
0
        trackedRunnable =
358
0
          aNamed ? NS_NewRunnableFunction("unused",
359
0
                                          [tracker]() mutable { tracker(); })
360
0
                 : NS_NewRunnableFunction("TestNewRunnableFunction",
361
0
                                          [tracker]() mutable { tracker(); });
362
0
        // Expect 1 copy (here -> local lambda) and 1 move (local -> runnable lambda).
363
0
      }
364
0
      trackedRunnable->Run();
365
0
    }
366
0
    Expect("NS_NewRunnableFunction with copyable&movable capture, copies",
367
0
           copyCounter, 1);
368
0
    Expect("NS_NewRunnableFunction with copyable&movable capture, moves",
369
0
           moveCounter, 1);
370
0
  }
371
0
}
372
373
TEST(ThreadUtils, NewRunnableFunction)
374
0
{
375
0
  TestNewRunnableFunction(/*aNamed*/false);
376
0
}
377
378
TEST(ThreadUtils, NewNamedRunnableFunction)
379
0
{
380
0
  // The named overload shall behave identical to the non-named counterpart.
381
0
  TestNewRunnableFunction(/*aNamed*/true);
382
0
383
0
  // Test naming.
384
0
  {
385
0
    const char* expectedName = "NamedRunnable";
386
0
    RefPtr<Runnable> NamedRunnable =
387
0
      NS_NewRunnableFunction(expectedName, [] {});
388
0
    ExpectRunnableName(NamedRunnable, expectedName);
389
0
  }
390
0
}
391
392
static void TestNewRunnableMethod(bool aNamed)
393
0
{
394
0
  memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool));
395
0
  // Scope the smart ptrs so that the runnables need to hold on to whatever they need
396
0
  {
397
0
    RefPtr<nsFoo> foo = new nsFoo();
398
0
    RefPtr<nsBar> bar = new nsBar();
399
0
    RefPtr<const nsBar> constBar = bar;
400
0
401
0
    // This pointer will be freed at the end of the block
402
0
    // Do not dereference this pointer in the runnable method!
403
0
    RefPtr<nsFoo> rawFoo = new nsFoo();
404
0
405
0
    // Read only string. Dereferencing in runnable method to check this works.
406
0
    char* message = (char*)"Test message";
407
0
408
0
    NS_DispatchToMainThread(
409
0
      aNamed ? NewRunnableMethod("unused", bar, &nsBar::DoBar1)
410
0
             : NewRunnableMethod("nsBar::DoBar1", bar, &nsBar::DoBar1));
411
0
    NS_DispatchToMainThread(
412
0
      aNamed ? NewRunnableMethod("unused", constBar, &nsBar::DoBar1Const)
413
0
             : NewRunnableMethod(
414
0
                 "nsBar::DoBar1Const", constBar, &nsBar::DoBar1Const));
415
0
    NS_DispatchToMainThread(
416
0
      aNamed ? NewRunnableMethod("unused", bar, &nsBar::DoBar2)
417
0
             : NewRunnableMethod("nsBar::DoBar2", bar, &nsBar::DoBar2));
418
0
    NS_DispatchToMainThread(
419
0
      aNamed
420
0
        ? NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar3, foo)
421
0
        : NewRunnableMethod<RefPtr<nsFoo>>(
422
0
            "nsBar::DoBar3", bar, &nsBar::DoBar3, foo));
423
0
    NS_DispatchToMainThread(
424
0
      aNamed
425
0
        ? NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar4, foo)
426
0
        : NewRunnableMethod<RefPtr<nsFoo>>(
427
0
            "nsBar::DoBar4", bar, &nsBar::DoBar4, foo));
428
0
    NS_DispatchToMainThread(
429
0
      aNamed ? NewRunnableMethod<nsFoo*>("unused", bar, &nsBar::DoBar5, rawFoo)
430
0
             : NewRunnableMethod<nsFoo*>(
431
0
                 "nsBar::DoBar5", bar, &nsBar::DoBar5, rawFoo));
432
0
    NS_DispatchToMainThread(
433
0
      aNamed ? NewRunnableMethod<char*>("unused", bar, &nsBar::DoBar6, message)
434
0
             : NewRunnableMethod<char*>(
435
0
                 "nsBar::DoBar6", bar, &nsBar::DoBar6, message));
436
#ifdef HAVE_STDCALL
437
    NS_DispatchToMainThread(aNamed ?
438
      NewRunnableMethod("unused", bar, &nsBar::DoBar1std) :
439
      NewRunnableMethod(bar, &nsBar::DoBar1std));
440
    NS_DispatchToMainThread(aNamed ?
441
      NewRunnableMethod("unused", bar, &nsBar::DoBar2std) :
442
      NewRunnableMethod(bar, &nsBar::DoBar2std));
443
    NS_DispatchToMainThread(aNamed ?
444
      NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar3std, foo) :
445
      NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar3std, foo));
446
    NS_DispatchToMainThread(aNamed ?
447
      NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar4std, foo) :
448
      NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar4std, foo));
449
    NS_DispatchToMainThread(aNamed ?
450
      NewRunnableMethod<nsFoo*>("unused", bar, &nsBar::DoBar5std, rawFoo) :
451
      NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5std, rawFoo));
452
    NS_DispatchToMainThread(aNamed ?
453
      NewRunnableMethod<char*>("unused", bar, &nsBar::DoBar6std, message) :
454
      NewRunnableMethod<char*>(bar, &nsBar::DoBar6std, message));
455
#endif
456
  }
457
0
458
0
  // Spin the event loop
459
0
  NS_ProcessPendingEvents(nullptr);
460
0
461
0
  // Now test a suicidal event in NS_New(Named)Thread
462
0
  nsCOMPtr<nsIThread> thread;
463
0
  NS_NewNamedThread("SuicideThread", getter_AddRefs(thread), new TestSuicide());
464
0
  ASSERT_TRUE(thread);
465
0
466
0
  while (!gRunnableExecuted[TEST_CALL_NEWTHREAD_SUICIDAL]) {
467
0
    NS_ProcessPendingEvents(nullptr);
468
0
  }
469
0
470
0
  for (uint32_t i = 0; i < MAX_TESTS; i++) {
471
0
    EXPECT_TRUE(gRunnableExecuted[i]) << "Error in test " << i;
472
0
  }
473
0
}
474
475
TEST(ThreadUtils, RunnableMethod)
476
0
{
477
0
  TestNewRunnableMethod(/* aNamed */false);
478
0
}
479
480
TEST(ThreadUtils, NamedRunnableMethod)
481
0
{
482
0
  // The named overloads shall behave identical to the non-named counterparts.
483
0
  TestNewRunnableMethod(/* aNamed */true);
484
0
485
0
  // Test naming.
486
0
  {
487
0
    RefPtr<nsFoo> foo = new nsFoo();
488
0
    const char* expectedName = "NamedRunnable";
489
0
    bool unused;
490
0
    RefPtr<Runnable> NamedRunnable =
491
0
      NewRunnableMethod<bool*>(expectedName, foo, &nsFoo::DoFoo, &unused);
492
0
    ExpectRunnableName(NamedRunnable, expectedName);
493
0
  }
494
0
}
495
496
class IdleObjectWithoutSetDeadline final
497
{
498
public:
499
  NS_INLINE_DECL_REFCOUNTING(IdleObjectWithoutSetDeadline)
500
  IdleObjectWithoutSetDeadline()
501
    : mRunnableExecuted(false)
502
0
  {
503
0
  }
504
0
  void Method() { mRunnableExecuted = true; }
505
  bool mRunnableExecuted;
506
507
private:
508
0
  ~IdleObjectWithoutSetDeadline() {}
509
};
510
511
class IdleObjectParentWithSetDeadline
512
{
513
public:
514
0
  IdleObjectParentWithSetDeadline() : mSetDeadlineCalled(false) {}
515
0
  void SetDeadline(TimeStamp aDeadline) { mSetDeadlineCalled = true; }
516
  bool mSetDeadlineCalled;
517
};
518
519
class IdleObjectInheritedSetDeadline final
520
  : public IdleObjectParentWithSetDeadline
521
{
522
public:
523
  NS_INLINE_DECL_REFCOUNTING(IdleObjectInheritedSetDeadline)
524
  IdleObjectInheritedSetDeadline()
525
    : mRunnableExecuted(false)
526
0
  {
527
0
  }
528
0
  void Method() { mRunnableExecuted = true; }
529
  bool mRunnableExecuted;
530
531
private:
532
0
  ~IdleObjectInheritedSetDeadline() {}
533
};
534
535
class IdleObject final
536
{
537
public:
538
  NS_INLINE_DECL_REFCOUNTING(IdleObject)
539
  IdleObject()
540
0
  {
541
0
    for (uint32_t index = 0; index < ArrayLength(mRunnableExecuted); ++index) {
542
0
      mRunnableExecuted[index] = false;
543
0
      mSetIdleDeadlineCalled = false;
544
0
    }
545
0
  }
546
0
  void SetDeadline(TimeStamp aTimeStamp) {
547
0
    mSetIdleDeadlineCalled = true;
548
0
  }
549
550
  void CheckExecutedMethods(const char* aKey, uint32_t aNumExecuted)
551
0
  {
552
0
    uint32_t index;
553
0
    for (index = 0; index < aNumExecuted; ++index) {
554
0
      ASSERT_TRUE(mRunnableExecuted[index])
555
0
          << aKey << ": Method" << index << " should've executed";
556
0
    }
557
0
558
0
    for (; index < ArrayLength(mRunnableExecuted); ++index) {
559
0
      ASSERT_FALSE(mRunnableExecuted[index])
560
0
        << aKey << ": Method" << index << " shouldn't have executed";
561
0
    }
562
0
  }
563
564
  void Method0()
565
0
  {
566
0
    CheckExecutedMethods("Method0", 0);
567
0
    mRunnableExecuted[0] = true;
568
0
    mSetIdleDeadlineCalled = false;
569
0
  }
570
571
  void Method1()
572
0
  {
573
0
    CheckExecutedMethods("Method1", 1);
574
0
    ASSERT_TRUE(mSetIdleDeadlineCalled);
575
0
    mRunnableExecuted[1] = true;
576
0
    mSetIdleDeadlineCalled = false;
577
0
  }
578
579
  void Method2()
580
0
  {
581
0
    CheckExecutedMethods("Method2", 2);
582
0
    ASSERT_TRUE(mSetIdleDeadlineCalled);
583
0
    mRunnableExecuted[2] = true;
584
0
    mSetIdleDeadlineCalled = false;
585
0
    NS_DispatchToCurrentThread(NewRunnableMethod("IdleObject::Method3", this, &IdleObject::Method3));
586
0
  }
587
588
  void Method3()
589
0
  {
590
0
    CheckExecutedMethods("Method3", 3);
591
0
592
0
    NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer),
593
0
                                Method4, this, 10, nsITimer::TYPE_ONE_SHOT,
594
0
                                "IdleObject::Method3");
595
0
    NS_IdleDispatchToCurrentThread(
596
0
      NewIdleRunnableMethodWithTimer("IdleObject::Method5", this, &IdleObject::Method5), 50);
597
0
    NS_IdleDispatchToCurrentThread(
598
0
      NewRunnableMethod("IdleObject::Method6", this, &IdleObject::Method6),
599
0
      100);
600
0
601
0
    PR_Sleep(PR_MillisecondsToInterval(200));
602
0
    mRunnableExecuted[3] = true;
603
0
    mSetIdleDeadlineCalled = false;
604
0
  }
605
606
  static void Method4(nsITimer* aTimer, void* aClosure)
607
0
  {
608
0
    RefPtr<IdleObject> self = static_cast<IdleObject*>(aClosure);
609
0
    self->CheckExecutedMethods("Method4", 4);
610
0
    self->mRunnableExecuted[4] = true;
611
0
    self->mSetIdleDeadlineCalled = false;
612
0
  }
613
614
  void Method5()
615
0
  {
616
0
    CheckExecutedMethods("Method5", 5);
617
0
    ASSERT_TRUE(mSetIdleDeadlineCalled);
618
0
    mRunnableExecuted[5] = true;
619
0
    mSetIdleDeadlineCalled = false;
620
0
  }
621
622
  void Method6()
623
0
  {
624
0
    CheckExecutedMethods("Method6", 6);
625
0
    mRunnableExecuted[6] = true;
626
0
    mSetIdleDeadlineCalled = false;
627
0
  }
628
629
  void Method7()
630
0
  {
631
0
    CheckExecutedMethods("Method7", 7);
632
0
    ASSERT_TRUE(mSetIdleDeadlineCalled);
633
0
    mRunnableExecuted[7] = true;
634
0
    mSetIdleDeadlineCalled = false;
635
0
  }
636
637
private:
638
  nsCOMPtr<nsITimer> mTimer;
639
  bool mRunnableExecuted[8];
640
  bool mSetIdleDeadlineCalled;
641
0
  ~IdleObject() {}
642
};
643
644
TEST(ThreadUtils, IdleRunnableMethod)
645
0
{
646
0
  {
647
0
    RefPtr<IdleObject> idle = new IdleObject();
648
0
    RefPtr<IdleObjectWithoutSetDeadline> idleNoSetDeadline =
649
0
      new IdleObjectWithoutSetDeadline();
650
0
    RefPtr<IdleObjectInheritedSetDeadline> idleInheritedSetDeadline =
651
0
      new IdleObjectInheritedSetDeadline();
652
0
653
0
    NS_DispatchToCurrentThread(
654
0
      NewRunnableMethod("IdleObject::Method0", idle, &IdleObject::Method0));
655
0
    NS_IdleDispatchToCurrentThread(
656
0
      NewIdleRunnableMethod("IdleObject::Method1", idle, &IdleObject::Method1));
657
0
    NS_IdleDispatchToCurrentThread(
658
0
      NewIdleRunnableMethodWithTimer("IdleObject::Method2", idle, &IdleObject::Method2), 60000);
659
0
    NS_IdleDispatchToCurrentThread(
660
0
      NewIdleRunnableMethod("IdleObject::Method7", idle, &IdleObject::Method7));
661
0
    NS_IdleDispatchToCurrentThread(NewIdleRunnableMethod<const char*, uint32_t>(
662
0
      "IdleObject::CheckExecutedMethods", idle, &IdleObject::CheckExecutedMethods, "final", 8));
663
0
    NS_IdleDispatchToCurrentThread(NewIdleRunnableMethod(
664
0
      "IdleObjectWithoutSetDeadline::Method",
665
0
      idleNoSetDeadline, &IdleObjectWithoutSetDeadline::Method));
666
0
    NS_IdleDispatchToCurrentThread(NewIdleRunnableMethod(
667
0
      "IdleObjectInheritedSetDeadline::Method",
668
0
      idleInheritedSetDeadline, &IdleObjectInheritedSetDeadline::Method));
669
0
670
0
    NS_ProcessPendingEvents(nullptr);
671
0
672
0
    ASSERT_TRUE(idleNoSetDeadline->mRunnableExecuted);
673
0
    ASSERT_TRUE(idleInheritedSetDeadline->mRunnableExecuted);
674
0
    ASSERT_TRUE(idleInheritedSetDeadline->mSetDeadlineCalled);
675
0
  }
676
0
}
677
678
TEST(ThreadUtils, IdleTaskRunner)
679
0
{
680
0
  using namespace mozilla;
681
0
682
0
  // Repeating.
683
0
  int cnt1 = 0;
684
0
  RefPtr<IdleTaskRunner> runner1 =
685
0
    IdleTaskRunner::Create([&cnt1](TimeStamp) { cnt1++; return true; },
686
0
                           "runner1",
687
0
                           10,
688
0
                           3,
689
0
                           true,
690
0
                           nullptr);
691
0
692
0
  // Non-repeating but callback always return false so it's still repeating.
693
0
  int cnt2 = 0;
694
0
  RefPtr<IdleTaskRunner> runner2 =
695
0
    IdleTaskRunner::Create([&cnt2](TimeStamp) { cnt2++; return false; },
696
0
                           "runner2",
697
0
                           10,
698
0
                           3,
699
0
                           false,
700
0
                           nullptr);
701
0
702
0
  // Repeating until cnt3 >= 2 by returning 'true' in MayStopProcessing callback.
703
0
  // The strategy is to stop repeating as early as possible so that
704
0
  // we are more probable to catch the bug if it didn't stop as expected.
705
0
  int cnt3 = 0;
706
0
  RefPtr<IdleTaskRunner> runner3 =
707
0
    IdleTaskRunner::Create([&cnt3](TimeStamp) { cnt3++; return true; },
708
0
                           "runner3",
709
0
                           10,
710
0
                           3,
711
0
                           true,
712
0
                           [&cnt3]{ return cnt3 >= 2; });
713
0
714
0
  // Non-repeating can callback return true so the callback will
715
0
  // be only run once.
716
0
  int cnt4 = 0;
717
0
  RefPtr<IdleTaskRunner> runner4 =
718
0
    IdleTaskRunner::Create([&cnt4](TimeStamp) { cnt4++; return true; },
719
0
                           "runner4",
720
0
                           10,
721
0
                           3,
722
0
                           false,
723
0
                           nullptr);
724
0
725
0
  // Firstly we wait until the two repeating tasks reach their limits.
726
0
  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cnt1 >= 100; }));
727
0
  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cnt2 >= 100; }));
728
0
729
0
  // At any point ==> 0 <= cnt3 <= 2 since MayStopProcessing() would return
730
0
  // true when cnt3 >= 2.
731
0
  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
732
0
    if (cnt3 > 2) {
733
0
      EXPECT_TRUE(false) << "MaybeContinueProcess() doesn't work.";
734
0
      return true; // Stop on failure.
735
0
    }
736
0
    return cnt3 == 2; // Stop finish if we have reached its max value.
737
0
  }));
738
0
739
0
  // At any point ==> 0 <= cnt4 <= 1 since this is a non-repeating
740
0
  // idle runner.
741
0
  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
742
0
    // At any point: 0 <= cnt4 <= 1
743
0
    if (cnt4 > 1) {
744
0
      EXPECT_TRUE(false) << "The 'mRepeating' flag doesn't work.";
745
0
      return true; // Stop on failure.
746
0
    }
747
0
    return cnt4 == 1;
748
0
  }));
749
0
750
0
  // The repeating timer with no "exit" condition requires an explicit
751
0
  // Cancel() call.
752
0
  runner1->Cancel();
753
0
}
754
755
// {9e70a320-be02-11d1-8031-006008159b5a}
756
#define NS_IFOO_IID \
757
  {0x9e70a320, 0xbe02, 0x11d1,    \
758
    {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
759
760
TEST(ThreadUtils, TypeTraits)
761
0
{
762
0
  static_assert(!mozilla::IsRefcountedSmartPointer<int>::value,
763
0
                "IsRefcountedSmartPointer<int> should be false");
764
0
  static_assert(mozilla::IsRefcountedSmartPointer<RefPtr<int>>::value,
765
0
                "IsRefcountedSmartPointer<RefPtr<...>> should be true");
766
0
  static_assert(mozilla::IsRefcountedSmartPointer<const RefPtr<int>>::value,
767
0
                "IsRefcountedSmartPointer<const RefPtr<...>> should be true");
768
0
  static_assert(mozilla::IsRefcountedSmartPointer<volatile RefPtr<int>>::value,
769
0
                "IsRefcountedSmartPointer<volatile RefPtr<...>> should be true");
770
0
  static_assert(mozilla::IsRefcountedSmartPointer<const volatile RefPtr<int>>::value,
771
0
               "IsRefcountedSmartPointer<const volatile RefPtr<...>> should be true");
772
0
  static_assert(mozilla::IsRefcountedSmartPointer<nsCOMPtr<int>>::value,
773
0
                "IsRefcountedSmartPointer<nsCOMPtr<...>> should be true");
774
0
  static_assert(mozilla::IsRefcountedSmartPointer<const nsCOMPtr<int>>::value,
775
0
                "IsRefcountedSmartPointer<const nsCOMPtr<...>> should be true");
776
0
  static_assert(mozilla::IsRefcountedSmartPointer<volatile nsCOMPtr<int>>::value,
777
0
                "IsRefcountedSmartPointer<volatile nsCOMPtr<...>> should be true");
778
0
  static_assert(mozilla::IsRefcountedSmartPointer<const volatile nsCOMPtr<int>>::value,
779
0
                "IsRefcountedSmartPointer<const volatile nsCOMPtr<...>> should be true");
780
0
781
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<int>::Type>::value,
782
0
                "RemoveSmartPointer<int>::Type should be int");
783
0
  static_assert(mozilla::IsSame<int*, mozilla::RemoveSmartPointer<int*>::Type>::value,
784
0
                "RemoveSmartPointer<int*>::Type should be int*");
785
0
  static_assert(mozilla::IsSame<UniquePtr<int>, mozilla::RemoveSmartPointer<UniquePtr<int>>::Type>::value,
786
0
                "RemoveSmartPointer<UniquePtr<int>>::Type should be UniquePtr<int>");
787
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<RefPtr<int>>::Type>::value,
788
0
                "RemoveSmartPointer<RefPtr<int>>::Type should be int");
789
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const RefPtr<int>>::Type>::value,
790
0
                "RemoveSmartPointer<const RefPtr<int>>::Type should be int");
791
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<volatile RefPtr<int>>::Type>::value,
792
0
                "RemoveSmartPointer<volatile RefPtr<int>>::Type should be int");
793
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const volatile RefPtr<int>>::Type>::value,
794
0
                "RemoveSmartPointer<const volatile RefPtr<int>>::Type should be int");
795
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<nsCOMPtr<int>>::Type>::value,
796
0
                "RemoveSmartPointer<nsCOMPtr<int>>::Type should be int");
797
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const nsCOMPtr<int>>::Type>::value,
798
0
                "RemoveSmartPointer<const nsCOMPtr<int>>::Type should be int");
799
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<volatile nsCOMPtr<int>>::Type>::value,
800
0
                "RemoveSmartPointer<volatile nsCOMPtr<int>>::Type should be int");
801
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const volatile nsCOMPtr<int>>::Type>::value,
802
0
                "RemoveSmartPointer<const volatile nsCOMPtr<int>>::Type should be int");
803
0
804
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<int>::Type>::value,
805
0
                "RemoveRawOrSmartPointer<int>::Type should be int");
806
0
  static_assert(mozilla::IsSame<UniquePtr<int>, mozilla::RemoveRawOrSmartPointer<UniquePtr<int>>::Type>::value,
807
0
                "RemoveRawOrSmartPointer<UniquePtr<int>>::Type should be UniquePtr<int>");
808
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<int*>::Type>::value,
809
0
                "RemoveRawOrSmartPointer<int*>::Type should be int");
810
0
  static_assert(mozilla::IsSame<const int, mozilla::RemoveRawOrSmartPointer<const int*>::Type>::value,
811
0
                "RemoveRawOrSmartPointer<const int*>::Type should be const int");
812
0
  static_assert(mozilla::IsSame<volatile int, mozilla::RemoveRawOrSmartPointer<volatile int*>::Type>::value,
813
0
                "RemoveRawOrSmartPointer<volatile int*>::Type should be volatile int");
814
0
  static_assert(mozilla::IsSame<const volatile int, mozilla::RemoveRawOrSmartPointer<const volatile int*>::Type>::value,
815
0
                "RemoveRawOrSmartPointer<const volatile int*>::Type should be const volatile int");
816
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<RefPtr<int>>::Type>::value,
817
0
                "RemoveRawOrSmartPointer<RefPtr<int>>::Type should be int");
818
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const RefPtr<int>>::Type>::value,
819
0
                "RemoveRawOrSmartPointer<const RefPtr<int>>::Type should be int");
820
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<volatile RefPtr<int>>::Type>::value,
821
0
                "RemoveRawOrSmartPointer<volatile RefPtr<int>>::Type should be int");
822
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const volatile RefPtr<int>>::Type>::value,
823
0
                "RemoveRawOrSmartPointer<const volatile RefPtr<int>>::Type should be int");
824
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<nsCOMPtr<int>>::Type>::value,
825
0
                "RemoveRawOrSmartPointer<nsCOMPtr<int>>::Type should be int");
826
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const nsCOMPtr<int>>::Type>::value,
827
0
                "RemoveRawOrSmartPointer<const nsCOMPtr<int>>::Type should be int");
828
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<volatile nsCOMPtr<int>>::Type>::value,
829
0
                "RemoveRawOrSmartPointer<volatile nsCOMPtr<int>>::Type should be int");
830
0
  static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const volatile nsCOMPtr<int>>::Type>::value,
831
0
                "RemoveRawOrSmartPointer<const volatile nsCOMPtr<int>>::Type should be int");
832
0
}
833
834
namespace TestThreadUtils {
835
836
static bool gDebug = false;
837
static int gAlive, gZombies;
838
static int gAllConstructions, gConstructions, gCopyConstructions,
839
           gMoveConstructions, gDestructions, gAssignments, gMoves;
840
struct Spy
841
{
842
  static void ClearActions()
843
0
  {
844
0
    gAllConstructions = gConstructions = gCopyConstructions
845
0
        = gMoveConstructions = gDestructions = gAssignments = gMoves = 0;
846
0
  }
847
  static void ClearAll()
848
0
  {
849
0
    ClearActions();
850
0
    gAlive = 0;
851
0
  }
852
853
  explicit Spy(int aID) : mID(aID)
854
0
  {
855
0
    ++gAlive; ++gAllConstructions; ++gConstructions;
856
0
    if (gDebug) { printf("Spy[%d@%p]()\n", mID, this); }
857
0
  }
858
  Spy(const Spy& o) : mID(o.mID)
859
0
  {
860
0
    ++gAlive; ++gAllConstructions; ++gCopyConstructions;
861
0
    if (gDebug) { printf("Spy[%d@%p](&[%d@%p])\n", mID, this, o.mID, &o); }
862
0
  }
863
  Spy(Spy&& o) : mID(o.mID)
864
0
  {
865
0
    o.mID = -o.mID;
866
0
    ++gZombies; ++gAllConstructions; ++gMoveConstructions;
867
0
    if (gDebug) { printf("Spy[%d@%p](&&[%d->%d@%p])\n", mID, this, -o.mID, o.mID, &o); }
868
0
  }
869
  ~Spy()
870
0
  {
871
0
    if (mID >= 0) { --gAlive; } else { --gZombies; } ++gDestructions;
872
0
    if (gDebug) { printf("~Spy[%d@%p]()\n", mID, this); }
873
0
    mID = 0;
874
0
  }
875
  Spy& operator=(const Spy& o)
876
0
  {
877
0
    ++gAssignments;
878
0
    if (gDebug) { printf("Spy[%d->%d@%p] = &[%d@%p]\n", mID, o.mID, this, o.mID, &o); }
879
0
    mID = o.mID;
880
0
    return *this;
881
0
  };
882
  Spy& operator=(Spy&& o)
883
0
  {
884
0
    --gAlive; ++gZombies;
885
0
    ++gMoves;
886
0
    if (gDebug) { printf("Spy[%d->%d@%p] = &&[%d->%d@%p]\n", mID, o.mID, this, o.mID, -o.mID, &o); }
887
0
    mID = o.mID; o.mID = -o.mID;
888
0
    return *this;
889
0
  };
890
891
  int mID; // ID given at construction, or negation if was moved from; 0 when destroyed.
892
};
893
894
struct ISpyWithISupports : public nsISupports
895
{
896
  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
897
  NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
898
  NS_IMETHOD_(int32_t) ID() = 0;
899
};
900
NS_DEFINE_STATIC_IID_ACCESSOR(ISpyWithISupports, NS_IFOO_IID)
901
struct SpyWithISupports : public ISpyWithISupports, public Spy
902
{
903
private:
904
0
  virtual ~SpyWithISupports() = default;
905
public:
906
0
  explicit SpyWithISupports(int aID) : Spy(aID) {};
907
  NS_DECL_ISUPPORTS
908
0
  NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
909
0
  NS_IMETHOD_(int32_t) ID() override { return mID; }
910
};
911
NS_IMPL_ISUPPORTS(SpyWithISupports, ISpyWithISupports)
912
913
914
class IThreadUtilsObject : public nsISupports
915
{
916
public:
917
  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
918
919
  NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
920
  NS_IMETHOD_(int32_t) ID() = 0;
921
};
922
923
NS_DEFINE_STATIC_IID_ACCESSOR(IThreadUtilsObject, NS_IFOO_IID)
924
925
struct ThreadUtilsObjectNonRefCountedBase
926
{
927
0
  virtual void MethodFromNonRefCountedBase() {}
928
};
929
930
struct ThreadUtilsObject : public IThreadUtilsObject
931
                         , public ThreadUtilsObjectNonRefCountedBase
932
{
933
  // nsISupports implementation
934
  NS_DECL_ISUPPORTS
935
936
  // IThreadUtilsObject implementation
937
0
  NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
938
0
  NS_IMETHOD_(int32_t) ID() override { return 0; }
939
940
  int mCount; // Number of calls + arguments processed.
941
  int mA0, mA1, mA2, mA3;
942
  Spy mSpy; const Spy* mSpyPtr;
943
  ThreadUtilsObject()
944
    : mCount(0)
945
    , mA0(0), mA1(0), mA2(0), mA3(0)
946
    , mSpy(1), mSpyPtr(nullptr)
947
0
  {}
948
private:
949
0
  virtual ~ThreadUtilsObject() = default;
950
public:
951
0
  void Test0() { mCount += 1; }
952
0
  void Test1i(int a0) { mCount += 2; mA0 = a0; }
953
0
  void Test2i(int a0, int a1) { mCount += 3; mA0 = a0; mA1 = a1; }
954
  void Test3i(int a0, int a1, int a2)
955
0
  {
956
0
    mCount += 4; mA0 = a0; mA1 = a1; mA2 = a2;
957
0
  }
958
  void Test4i(int a0, int a1, int a2, int a3)
959
0
  {
960
0
    mCount += 5; mA0 = a0; mA1 = a1; mA2 = a2; mA3 = a3;
961
0
  }
962
  void Test1pi(int* ap)
963
0
  {
964
0
    mCount += 2; mA0 = ap ? *ap : -1;
965
0
  }
966
  void Test1pci(const int* ap)
967
0
  {
968
0
    mCount += 2; mA0 = ap ? *ap : -1;
969
0
  }
970
  void Test1ri(int& ar)
971
0
  {
972
0
    mCount += 2; mA0 = ar;
973
0
  }
974
  void Test1rri(int&& arr)
975
0
  {
976
0
    mCount += 2; mA0 = arr;
977
0
  }
978
  void Test1upi(mozilla::UniquePtr<int> aup)
979
0
  {
980
0
    mCount += 2; mA0 = aup ? *aup : -1;
981
0
  }
982
  void Test1rupi(mozilla::UniquePtr<int>& aup)
983
0
  {
984
0
    mCount += 2; mA0 = aup ? *aup : -1;
985
0
  }
986
  void Test1rrupi(mozilla::UniquePtr<int>&& aup)
987
0
  {
988
0
    mCount += 2; mA0 = aup ? *aup : -1;
989
0
  }
990
991
0
  void Test1s(Spy) { mCount += 2; }
992
0
  void Test1ps(Spy*) { mCount += 2; }
993
0
  void Test1rs(Spy&) { mCount += 2; }
994
0
  void Test1rrs(Spy&&) { mCount += 2; }
995
0
  void Test1ups(mozilla::UniquePtr<Spy>) { mCount += 2; }
996
0
  void Test1rups(mozilla::UniquePtr<Spy>&) { mCount += 2; }
997
0
  void Test1rrups(mozilla::UniquePtr<Spy>&&) { mCount += 2; }
998
999
  // Possible parameter passing styles:
1000
  void TestByValue(Spy s)
1001
0
  {
1002
0
    if (gDebug) { printf("TestByValue(Spy[%d@%p])\n", s.mID, &s); }
1003
0
    mSpy = s;
1004
0
  };
1005
  void TestByConstLRef(const Spy& s)
1006
0
  {
1007
0
    if (gDebug) { printf("TestByConstLRef(Spy[%d@%p]&)\n", s.mID, &s); }
1008
0
    mSpy = s;
1009
0
  };
1010
  void TestByRRef(Spy&& s)
1011
0
  {
1012
0
    if (gDebug) { printf("TestByRRef(Spy[%d@%p]&&)\n", s.mID, &s); }
1013
0
    mSpy = std::move(s);
1014
0
  };
1015
  void TestByLRef(Spy& s)
1016
0
  {
1017
0
    if (gDebug) { printf("TestByLRef(Spy[%d@%p]&)\n", s.mID, &s); }
1018
0
    mSpy = s;
1019
0
    mSpyPtr = &s;
1020
0
  };
1021
  void TestByPointer(Spy* p)
1022
0
  {
1023
0
    if (p) {
1024
0
      if (gDebug) { printf("TestByPointer(&Spy[%d@%p])\n", p->mID, p); }
1025
0
      mSpy = *p;
1026
0
    } else {
1027
0
      if (gDebug) { printf("TestByPointer(nullptr)\n"); }
1028
0
    }
1029
0
    mSpyPtr = p;
1030
0
  };
1031
  void TestByPointerToConst(const Spy* p)
1032
0
  {
1033
0
    if (p) {
1034
0
      if (gDebug) { printf("TestByPointerToConst(&Spy[%d@%p])\n", p->mID, p); }
1035
0
      mSpy = *p;
1036
0
    } else {
1037
0
      if (gDebug) { printf("TestByPointerToConst(nullptr)\n"); }
1038
0
    }
1039
0
    mSpyPtr = p;
1040
0
  };
1041
};
1042
1043
NS_IMPL_ISUPPORTS(ThreadUtilsObject, IThreadUtilsObject)
1044
1045
class ThreadUtilsRefCountedFinal final
1046
{
1047
public:
1048
0
  ThreadUtilsRefCountedFinal() : m_refCount(0) {}
1049
0
  ~ThreadUtilsRefCountedFinal() {}
1050
  // 'AddRef' and 'Release' methods with different return types, to verify
1051
  // that the return type doesn't influence storage selection.
1052
0
  long AddRef(void) { return ++m_refCount; }
1053
0
  void Release(void) { --m_refCount; }
1054
private:
1055
  long m_refCount;
1056
};
1057
1058
class ThreadUtilsRefCountedBase
1059
{
1060
public:
1061
0
  ThreadUtilsRefCountedBase() : m_refCount(0) {}
1062
0
  virtual ~ThreadUtilsRefCountedBase() {}
1063
  // 'AddRef' and 'Release' methods with different return types, to verify
1064
  // that the return type doesn't influence storage selection.
1065
0
  virtual void AddRef(void) { ++m_refCount; }
1066
0
  virtual MozExternalRefCountType Release(void) { return --m_refCount; }
1067
private:
1068
  MozExternalRefCountType m_refCount;
1069
};
1070
1071
class ThreadUtilsRefCountedDerived
1072
  : public ThreadUtilsRefCountedBase
1073
{};
1074
1075
class ThreadUtilsNonRefCounted
1076
{};
1077
1078
} // namespace TestThreadUtils
1079
1080
TEST(ThreadUtils, main)
1081
0
{
1082
0
  using namespace TestThreadUtils;
1083
0
1084
0
  static_assert(!IsParameterStorageClass<int>::value,
1085
0
                "'int' should not be recognized as Storage Class");
1086
0
  static_assert(IsParameterStorageClass<StoreCopyPassByValue<int>>::value,
1087
0
                "StoreCopyPassByValue<int> should be recognized as Storage Class");
1088
0
  static_assert(IsParameterStorageClass<StoreCopyPassByConstLRef<int>>::value,
1089
0
                "StoreCopyPassByConstLRef<int> should be recognized as Storage Class");
1090
0
  static_assert(IsParameterStorageClass<StoreCopyPassByLRef<int>>::value,
1091
0
                "StoreCopyPassByLRef<int> should be recognized as Storage Class");
1092
0
  static_assert(IsParameterStorageClass<StoreCopyPassByRRef<int>>::value,
1093
0
                "StoreCopyPassByRRef<int> should be recognized as Storage Class");
1094
0
  static_assert(IsParameterStorageClass<StoreRefPassByLRef<int>>::value,
1095
0
                "StoreRefPassByLRef<int> should be recognized as Storage Class");
1096
0
  static_assert(IsParameterStorageClass<StoreConstRefPassByConstLRef<int>>::value,
1097
0
                "StoreConstRefPassByConstLRef<int> should be recognized as Storage Class");
1098
0
  static_assert(IsParameterStorageClass<StoreRefPtrPassByPtr<int>>::value,
1099
0
                "StoreRefPtrPassByPtr<int> should be recognized as Storage Class");
1100
0
  static_assert(IsParameterStorageClass<StorePtrPassByPtr<int>>::value,
1101
0
                "StorePtrPassByPtr<int> should be recognized as Storage Class");
1102
0
  static_assert(IsParameterStorageClass<StoreConstPtrPassByConstPtr<int>>::value,
1103
0
                "StoreConstPtrPassByConstPtr<int> should be recognized as Storage Class");
1104
0
  static_assert(IsParameterStorageClass<StoreCopyPassByConstPtr<int>>::value,
1105
0
                "StoreCopyPassByConstPtr<int> should be recognized as Storage Class");
1106
0
  static_assert(IsParameterStorageClass<StoreCopyPassByPtr<int>>::value,
1107
0
                "StoreCopyPassByPtr<int> should be recognized as Storage Class");
1108
0
1109
0
  RefPtr<ThreadUtilsObject> rpt(new ThreadUtilsObject);
1110
0
  int count = 0;
1111
0
1112
0
  // Test legacy functions.
1113
0
1114
0
  nsCOMPtr<nsIRunnable> r1 =
1115
0
    NewRunnableMethod("TestThreadUtils::ThreadUtilsObject::Test0",
1116
0
                      rpt,
1117
0
                      &ThreadUtilsObject::Test0);
1118
0
  r1->Run();
1119
0
  EXPECT_EQ(count += 1, rpt->mCount);
1120
0
1121
0
  r1 = NewRunnableMethod<int>("TestThreadUtils::ThreadUtilsObject::Test1i",
1122
0
                              rpt,
1123
0
                              &ThreadUtilsObject::Test1i,
1124
0
                              11);
1125
0
  r1->Run();
1126
0
  EXPECT_EQ(count += 2, rpt->mCount);
1127
0
  EXPECT_EQ(11, rpt->mA0);
1128
0
1129
0
  // Test calling a method from a non-ref-counted base.
1130
0
1131
0
  r1 = NewRunnableMethod("TestThreadUtils::ThreadUtilsObjectNonRefCountedBase::"
1132
0
                         "MethodFromNonRefCountedBase",
1133
0
                         rpt,
1134
0
                         &ThreadUtilsObject::MethodFromNonRefCountedBase);
1135
0
  r1->Run();
1136
0
  EXPECT_EQ(count, rpt->mCount);
1137
0
1138
0
  // Test variadic function with simple POD arguments.
1139
0
1140
0
  r1 = NewRunnableMethod("TestThreadUtils::ThreadUtilsObject::Test0",
1141
0
                         rpt,
1142
0
                         &ThreadUtilsObject::Test0);
1143
0
  r1->Run();
1144
0
  EXPECT_EQ(count += 1, rpt->mCount);
1145
0
1146
0
  static_assert(
1147
0
      mozilla::IsSame< ::detail::ParameterStorage<int>::Type,
1148
0
                      StoreCopyPassByConstLRef<int>>::value,
1149
0
      "detail::ParameterStorage<int>::Type should be StoreCopyPassByConstLRef<int>");
1150
0
  static_assert(
1151
0
      mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByValue<int>>::Type,
1152
0
                      StoreCopyPassByValue<int>>::value,
1153
0
      "detail::ParameterStorage<StoreCopyPassByValue<int>>::Type should be StoreCopyPassByValue<int>");
1154
0
1155
0
  r1 = NewRunnableMethod<int>("TestThreadUtils::ThreadUtilsObject::Test1i",
1156
0
                              rpt,
1157
0
                              &ThreadUtilsObject::Test1i,
1158
0
                              12);
1159
0
  r1->Run();
1160
0
  EXPECT_EQ(count += 2, rpt->mCount);
1161
0
  EXPECT_EQ(12, rpt->mA0);
1162
0
1163
0
  r1 = NewRunnableMethod<int, int>("TestThreadUtils::ThreadUtilsObject::Test2i",
1164
0
                                   rpt,
1165
0
                                   &ThreadUtilsObject::Test2i,
1166
0
                                   21,
1167
0
                                   22);
1168
0
  r1->Run();
1169
0
  EXPECT_EQ(count += 3, rpt->mCount);
1170
0
  EXPECT_EQ(21, rpt->mA0);
1171
0
  EXPECT_EQ(22, rpt->mA1);
1172
0
1173
0
  r1 = NewRunnableMethod<int, int, int>(
1174
0
    "TestThreadUtils::ThreadUtilsObject::Test3i",
1175
0
    rpt,
1176
0
    &ThreadUtilsObject::Test3i,
1177
0
    31,
1178
0
    32,
1179
0
    33);
1180
0
  r1->Run();
1181
0
  EXPECT_EQ(count += 4, rpt->mCount);
1182
0
  EXPECT_EQ(31, rpt->mA0);
1183
0
  EXPECT_EQ(32, rpt->mA1);
1184
0
  EXPECT_EQ(33, rpt->mA2);
1185
0
1186
0
  r1 = NewRunnableMethod<int, int, int, int>(
1187
0
    "TestThreadUtils::ThreadUtilsObject::Test4i",
1188
0
    rpt,
1189
0
    &ThreadUtilsObject::Test4i,
1190
0
    41,
1191
0
    42,
1192
0
    43,
1193
0
    44);
1194
0
  r1->Run();
1195
0
  EXPECT_EQ(count += 5, rpt->mCount);
1196
0
  EXPECT_EQ(41, rpt->mA0);
1197
0
  EXPECT_EQ(42, rpt->mA1);
1198
0
  EXPECT_EQ(43, rpt->mA2);
1199
0
  EXPECT_EQ(44, rpt->mA3);
1200
0
1201
0
  // More interesting types of arguments.
1202
0
1203
0
  // Passing a short to make sure forwarding works with an inexact type match.
1204
0
  short int si = 11;
1205
0
  r1 = NewRunnableMethod<int>("TestThreadUtils::ThreadUtilsObject::Test1i",
1206
0
                              rpt,
1207
0
                              &ThreadUtilsObject::Test1i,
1208
0
                              si);
1209
0
  r1->Run();
1210
0
  EXPECT_EQ(count += 2, rpt->mCount);
1211
0
  EXPECT_EQ(si, rpt->mA0);
1212
0
1213
0
  // Raw pointer, possible cv-qualified.
1214
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type,
1215
0
                                StorePtrPassByPtr<int>>::value,
1216
0
                "detail::ParameterStorage<int*>::Type should be StorePtrPassByPtr<int>");
1217
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const>::Type,
1218
0
                                StorePtrPassByPtr<int>>::value,
1219
0
                "detail::ParameterStorage<int* const>::Type should be StorePtrPassByPtr<int>");
1220
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* volatile>::Type,
1221
0
                                StorePtrPassByPtr<int>>::value,
1222
0
                "detail::ParameterStorage<int* volatile>::Type should be StorePtrPassByPtr<int>");
1223
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const volatile>::Type,
1224
0
                                StorePtrPassByPtr<int>>::value,
1225
0
                "detail::ParameterStorage<int* const volatile>::Type should be StorePtrPassByPtr<int>");
1226
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::stored_type,
1227
0
                                int*>::value,
1228
0
                "detail::ParameterStorage<int*>::Type::stored_type should be int*");
1229
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::passed_type,
1230
0
                                int*>::value,
1231
0
                "detail::ParameterStorage<int*>::Type::passed_type should be int*");
1232
0
  {
1233
0
    int i = 12;
1234
0
    r1 = NewRunnableMethod<int*>("TestThreadUtils::ThreadUtilsObject::Test1pi",
1235
0
                                 rpt,
1236
0
                                 &ThreadUtilsObject::Test1pi,
1237
0
                                 &i);
1238
0
    r1->Run();
1239
0
    EXPECT_EQ(count += 2, rpt->mCount);
1240
0
    EXPECT_EQ(i, rpt->mA0);
1241
0
  }
1242
0
1243
0
  // Raw pointer to const.
1244
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type,
1245
0
                                StoreConstPtrPassByConstPtr<int>>::value,
1246
0
                "detail::ParameterStorage<const int*>::Type should be StoreConstPtrPassByConstPtr<int>");
1247
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const>::Type,
1248
0
                                StoreConstPtrPassByConstPtr<int>>::value,
1249
0
                "detail::ParameterStorage<const int* const>::Type should be StoreConstPtrPassByConstPtr<int>");
1250
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* volatile>::Type,
1251
0
                                StoreConstPtrPassByConstPtr<int>>::value,
1252
0
                "detail::ParameterStorage<const int* volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
1253
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const volatile>::Type,
1254
0
                                StoreConstPtrPassByConstPtr<int>>::value,
1255
0
                "detail::ParameterStorage<const int* const volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
1256
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::stored_type,
1257
0
                                const int*>::value,
1258
0
                "detail::ParameterStorage<const int*>::Type::stored_type should be const int*");
1259
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::passed_type,
1260
0
                                const int*>::value,
1261
0
                "detail::ParameterStorage<const int*>::Type::passed_type should be const int*");
1262
0
  {
1263
0
    int i = 1201;
1264
0
    r1 = NewRunnableMethod<const int*>(
1265
0
      "TestThreadUtils::ThreadUtilsObject::Test1pci",
1266
0
      rpt,
1267
0
      &ThreadUtilsObject::Test1pci,
1268
0
      &i);
1269
0
    r1->Run();
1270
0
    EXPECT_EQ(count += 2, rpt->mCount);
1271
0
    EXPECT_EQ(i, rpt->mA0);
1272
0
  }
1273
0
1274
0
  // Raw pointer to copy.
1275
0
  static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::stored_type,
1276
0
                                int>::value,
1277
0
                "StoreCopyPassByPtr<int>::stored_type should be int");
1278
0
  static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::passed_type,
1279
0
                                int*>::value,
1280
0
                "StoreCopyPassByPtr<int>::passed_type should be int*");
1281
0
  {
1282
0
    int i = 1202;
1283
0
    r1 = NewRunnableMethod<StoreCopyPassByPtr<int>>(
1284
0
      "TestThreadUtils::ThreadUtilsObject::Test1pi",
1285
0
      rpt,
1286
0
      &ThreadUtilsObject::Test1pi,
1287
0
      i);
1288
0
    r1->Run();
1289
0
    EXPECT_EQ(count += 2, rpt->mCount);
1290
0
    EXPECT_EQ(i, rpt->mA0);
1291
0
  }
1292
0
1293
0
  // Raw pointer to const copy.
1294
0
  static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::stored_type,
1295
0
                                int>::value,
1296
0
                "StoreCopyPassByConstPtr<int>::stored_type should be int");
1297
0
  static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::passed_type,
1298
0
                                const int*>::value,
1299
0
                "StoreCopyPassByConstPtr<int>::passed_type should be const int*");
1300
0
  {
1301
0
    int i = 1203;
1302
0
    r1 = NewRunnableMethod<StoreCopyPassByConstPtr<int>>(
1303
0
      "TestThreadUtils::ThreadUtilsObject::Test1pci",
1304
0
      rpt,
1305
0
      &ThreadUtilsObject::Test1pci,
1306
0
      i);
1307
0
    r1->Run();
1308
0
    EXPECT_EQ(count += 2, rpt->mCount);
1309
0
    EXPECT_EQ(i, rpt->mA0);
1310
0
  }
1311
0
1312
0
  // nsRefPtr to pointer.
1313
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreRefPtrPassByPtr<SpyWithISupports>>::Type,
1314
0
                                StoreRefPtrPassByPtr<SpyWithISupports>>::value,
1315
0
                "ParameterStorage<StoreRefPtrPassByPtr<SpyWithISupports>>::Type should be StoreRefPtrPassByPtr<SpyWithISupports>");
1316
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<SpyWithISupports*>::Type,
1317
0
                                StoreRefPtrPassByPtr<SpyWithISupports>>::value,
1318
0
                "ParameterStorage<SpyWithISupports*>::Type should be StoreRefPtrPassByPtr<SpyWithISupports>");
1319
0
  static_assert(mozilla::IsSame<StoreRefPtrPassByPtr<SpyWithISupports>::stored_type,
1320
0
                                RefPtr<SpyWithISupports>>::value,
1321
0
                "StoreRefPtrPassByPtr<SpyWithISupports>::stored_type should be RefPtr<SpyWithISupports>");
1322
0
  static_assert(mozilla::IsSame<StoreRefPtrPassByPtr<SpyWithISupports>::passed_type,
1323
0
                                SpyWithISupports*>::value,
1324
0
                "StoreRefPtrPassByPtr<SpyWithISupports>::passed_type should be SpyWithISupports*");
1325
0
  // (more nsRefPtr tests below)
1326
0
1327
0
  // nsRefPtr for ref-countable classes that do not derive from ISupports.
1328
0
  static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedFinal>::value,
1329
0
                "ThreadUtilsRefCountedFinal has AddRef() and Release()");
1330
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedFinal*>::Type,
1331
0
                                StoreRefPtrPassByPtr<ThreadUtilsRefCountedFinal>>::value,
1332
0
                "ParameterStorage<ThreadUtilsRefCountedFinal*>::Type should be StoreRefPtrPassByPtr<ThreadUtilsRefCountedFinal>");
1333
0
  static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedBase>::value,
1334
0
                "ThreadUtilsRefCountedBase has AddRef() and Release()");
1335
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedBase*>::Type,
1336
0
                                StoreRefPtrPassByPtr<ThreadUtilsRefCountedBase>>::value,
1337
0
                "ParameterStorage<ThreadUtilsRefCountedBase*>::Type should be StoreRefPtrPassByPtr<ThreadUtilsRefCountedBase>");
1338
0
  static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedDerived>::value,
1339
0
                "ThreadUtilsRefCountedDerived has AddRef() and Release()");
1340
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedDerived*>::Type,
1341
0
                                StoreRefPtrPassByPtr<ThreadUtilsRefCountedDerived>>::value,
1342
0
                "ParameterStorage<ThreadUtilsRefCountedDerived*>::Type should be StoreRefPtrPassByPtr<ThreadUtilsRefCountedDerived>");
1343
0
1344
0
  static_assert(!::detail::HasRefCountMethods<ThreadUtilsNonRefCounted>::value,
1345
0
                "ThreadUtilsNonRefCounted doesn't have AddRef() and Release()");
1346
0
  static_assert(!mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsNonRefCounted*>::Type,
1347
0
                                 StoreRefPtrPassByPtr<ThreadUtilsNonRefCounted>>::value,
1348
0
                "ParameterStorage<ThreadUtilsNonRefCounted*>::Type should NOT be StoreRefPtrPassByPtr<ThreadUtilsNonRefCounted>");
1349
0
1350
0
  // Lvalue reference.
1351
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type,
1352
0
                                StoreRefPassByLRef<int>>::value,
1353
0
                "ParameterStorage<int&>::Type should be StoreRefPassByLRef<int>");
1354
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
1355
0
                                StoreRefPassByLRef<int>::stored_type>::value,
1356
0
                "ParameterStorage<int&>::Type::stored_type should be StoreRefPassByLRef<int>::stored_type");
1357
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
1358
0
                                int&>::value,
1359
0
                "ParameterStorage<int&>::Type::stored_type should be int&");
1360
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::passed_type,
1361
0
                                int&>::value,
1362
0
                "ParameterStorage<int&>::Type::passed_type should be int&");
1363
0
  {
1364
0
    int i = 13;
1365
0
    r1 = NewRunnableMethod<int&>("TestThreadUtils::ThreadUtilsObject::Test1ri",
1366
0
                                 rpt,
1367
0
                                 &ThreadUtilsObject::Test1ri,
1368
0
                                 i);
1369
0
    r1->Run();
1370
0
    EXPECT_EQ(count += 2, rpt->mCount);
1371
0
    EXPECT_EQ(i, rpt->mA0);
1372
0
  }
1373
0
1374
0
  // Rvalue reference -- Actually storing a copy and then moving it.
1375
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type,
1376
0
                                StoreCopyPassByRRef<int>>::value,
1377
0
                "ParameterStorage<int&&>::Type should be StoreCopyPassByRRef<int>");
1378
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
1379
0
                                StoreCopyPassByRRef<int>::stored_type>::value,
1380
0
                "ParameterStorage<int&&>::Type::stored_type should be StoreCopyPassByRRef<int>::stored_type");
1381
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
1382
0
                                int>::value,
1383
0
                "ParameterStorage<int&&>::Type::stored_type should be int");
1384
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::passed_type,
1385
0
                                int&&>::value,
1386
0
                "ParameterStorage<int&&>::Type::passed_type should be int&&");
1387
0
  {
1388
0
    int i = 14;
1389
0
    r1 =
1390
0
      NewRunnableMethod<int&&>("TestThreadUtils::ThreadUtilsObject::Test1rri",
1391
0
                               rpt,
1392
0
                               &ThreadUtilsObject::Test1rri,
1393
0
                               std::move(i));
1394
0
  }
1395
0
  r1->Run();
1396
0
  EXPECT_EQ(count += 2, rpt->mCount);
1397
0
  EXPECT_EQ(14, rpt->mA0);
1398
0
1399
0
  // Null unique pointer, by semi-implicit store&move with "T&&" syntax.
1400
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type,
1401
0
                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::value,
1402
0
                "ParameterStorage<UniquePtr<int>&&>::Type should be StoreCopyPassByRRef<UniquePtr<int>>");
1403
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
1404
0
                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
1405
0
                "ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
1406
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
1407
0
                                mozilla::UniquePtr<int>>::value,
1408
0
                "ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be UniquePtr<int>");
1409
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::passed_type,
1410
0
                                mozilla::UniquePtr<int>&&>::value,
1411
0
                "ParameterStorage<UniquePtr<int>&&>::Type::passed_type should be UniquePtr<int>&&");
1412
0
  {
1413
0
    mozilla::UniquePtr<int> upi;
1414
0
    r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
1415
0
      "TestThreadUtils::ThreadUtilsObject::Test1upi",
1416
0
      rpt,
1417
0
      &ThreadUtilsObject::Test1upi,
1418
0
      std::move(upi));
1419
0
  }
1420
0
  r1->Run();
1421
0
  EXPECT_EQ(count += 2, rpt->mCount);
1422
0
  EXPECT_EQ(-1, rpt->mA0);
1423
0
  rpt->mA0 = 0;
1424
0
1425
0
  // Null unique pointer, by explicit store&move with "StoreCopyPassByRRef<T>" syntax.
1426
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
1427
0
                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
1428
0
                "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
1429
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
1430
0
                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
1431
0
                "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
1432
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
1433
0
                                mozilla::UniquePtr<int>>::value,
1434
0
                "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be UniquePtr<int>");
1435
0
  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::passed_type,
1436
0
                                mozilla::UniquePtr<int>&&>::value,
1437
0
                "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::passed_type should be UniquePtr<int>&&");
1438
0
  {
1439
0
    mozilla::UniquePtr<int> upi;
1440
0
    r1 = NewRunnableMethod<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>(
1441
0
      "TestThreadUtils::ThreadUtilsObject::Test1upi",
1442
0
      rpt,
1443
0
      &ThreadUtilsObject::Test1upi,
1444
0
      std::move(upi));
1445
0
  }
1446
0
  r1->Run();
1447
0
  EXPECT_EQ(count += 2, rpt->mCount);
1448
0
  EXPECT_EQ(-1, rpt->mA0);
1449
0
1450
0
  // Unique pointer as xvalue.
1451
0
  {
1452
0
    mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
1453
0
    r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
1454
0
      "TestThreadUtils::ThreadUtilsObject::Test1upi",
1455
0
      rpt,
1456
0
      &ThreadUtilsObject::Test1upi,
1457
0
      std::move(upi));
1458
0
  }
1459
0
  r1->Run();
1460
0
  EXPECT_EQ(count += 2, rpt->mCount);
1461
0
  EXPECT_EQ(1, rpt->mA0);
1462
0
1463
0
  {
1464
0
    mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
1465
0
    r1 = NewRunnableMethod<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>(
1466
0
      "TestThreadUtils::ThreadUtilsObject::Test1upi",
1467
0
      rpt,
1468
0
      &ThreadUtilsObject::Test1upi,
1469
0
      std::move(upi));
1470
0
  }
1471
0
  r1->Run();
1472
0
  EXPECT_EQ(count += 2, rpt->mCount);
1473
0
  EXPECT_EQ(1, rpt->mA0);
1474
0
1475
0
  // Unique pointer as prvalue.
1476
0
  r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
1477
0
    "TestThreadUtils::ThreadUtilsObject::Test1upi",
1478
0
    rpt,
1479
0
    &ThreadUtilsObject::Test1upi,
1480
0
    mozilla::MakeUnique<int>(2));
1481
0
  r1->Run();
1482
0
  EXPECT_EQ(count += 2, rpt->mCount);
1483
0
  EXPECT_EQ(2, rpt->mA0);
1484
0
1485
0
  // Unique pointer as lvalue to lref.
1486
0
  {
1487
0
    mozilla::UniquePtr<int> upi;
1488
0
    r1 = NewRunnableMethod<mozilla::UniquePtr<int>&>(
1489
0
      "TestThreadUtils::ThreadUtilsObject::Test1rupi",
1490
0
      rpt,
1491
0
      &ThreadUtilsObject::Test1rupi,
1492
0
      upi);
1493
0
    // Passed as lref, so Run() must be called while local upi is still alive!
1494
0
    r1->Run();
1495
0
  }
1496
0
  EXPECT_EQ(count += 2, rpt->mCount);
1497
0
  EXPECT_EQ(-1, rpt->mA0);
1498
0
1499
0
  // Verify copy/move assumptions.
1500
0
1501
0
  Spy::ClearAll();
1502
0
  if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by value\n", __LINE__); }
1503
0
  { // Block around nsCOMPtr lifetime.
1504
0
    nsCOMPtr<nsIRunnable> r2;
1505
0
    { // Block around Spy lifetime.
1506
0
      if (gDebug) { printf("%d - Spy s(10)\n", __LINE__); }
1507
0
      Spy s(10);
1508
0
      EXPECT_EQ(1, gConstructions);
1509
0
      EXPECT_EQ(1, gAlive);
1510
0
      if (gDebug) { printf("%d - r2 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(&TestByValue, s)\n", __LINE__); }
1511
0
      r2 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
1512
0
        "TestThreadUtils::ThreadUtilsObject::TestByValue",
1513
0
        rpt,
1514
0
        &ThreadUtilsObject::TestByValue,
1515
0
        s);
1516
0
      EXPECT_EQ(2, gAlive);
1517
0
      EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
1518
0
      Spy::ClearActions();
1519
0
      if (gDebug) { printf("%d - End block with Spy s(10)\n", __LINE__); }
1520
0
    }
1521
0
    EXPECT_EQ(1, gDestructions);
1522
0
    EXPECT_EQ(1, gAlive);
1523
0
    Spy::ClearActions();
1524
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1525
0
    r2->Run();
1526
0
    EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
1527
0
    EXPECT_EQ(10, rpt->mSpy.mID);
1528
0
    EXPECT_LE(1, gDestructions);
1529
0
    EXPECT_EQ(1, gAlive);
1530
0
    Spy::ClearActions();
1531
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1532
0
  }
1533
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1534
0
  EXPECT_EQ(1, gDestructions);
1535
0
  EXPECT_EQ(0, gAlive);
1536
0
1537
0
  Spy::ClearAll();
1538
0
  if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by value\n", __LINE__); }
1539
0
  {
1540
0
    if (gDebug) { printf("%d - r3 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(&TestByValue, Spy(11))\n", __LINE__); }
1541
0
    nsCOMPtr<nsIRunnable> r3 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
1542
0
      "TestThreadUtils::ThreadUtilsObject::TestByValue",
1543
0
      rpt,
1544
0
      &ThreadUtilsObject::TestByValue,
1545
0
      Spy(11));
1546
0
    EXPECT_EQ(1, gAlive);
1547
0
    EXPECT_EQ(1, gConstructions);
1548
0
    EXPECT_LE(1, gMoveConstructions);
1549
0
    Spy::ClearActions();
1550
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1551
0
    r3->Run();
1552
0
    EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
1553
0
    EXPECT_EQ(11, rpt->mSpy.mID);
1554
0
    EXPECT_LE(1, gDestructions);
1555
0
    EXPECT_EQ(1, gAlive);
1556
0
    Spy::ClearActions();
1557
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1558
0
  }
1559
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1560
0
  EXPECT_EQ(1, gDestructions);
1561
0
  EXPECT_EQ(0, gAlive);
1562
0
1563
0
  Spy::ClearAll();
1564
0
  { // Store copy from xvalue, pass by value.
1565
0
    nsCOMPtr<nsIRunnable> r4;
1566
0
    {
1567
0
      Spy s(12);
1568
0
      EXPECT_EQ(1, gConstructions);
1569
0
      EXPECT_EQ(1, gAlive);
1570
0
      Spy::ClearActions();
1571
0
      r4 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
1572
0
        "TestThreadUtils::ThreadUtilsObject::TestByValue",
1573
0
        rpt,
1574
0
        &ThreadUtilsObject::TestByValue,
1575
0
        std::move(s));
1576
0
      EXPECT_LE(1, gMoveConstructions);
1577
0
      EXPECT_EQ(1, gAlive);
1578
0
      EXPECT_EQ(1, gZombies);
1579
0
      Spy::ClearActions();
1580
0
    }
1581
0
    EXPECT_EQ(1, gDestructions);
1582
0
    EXPECT_EQ(1, gAlive);
1583
0
    EXPECT_EQ(0, gZombies);
1584
0
    Spy::ClearActions();
1585
0
    r4->Run();
1586
0
    EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
1587
0
    EXPECT_EQ(12, rpt->mSpy.mID);
1588
0
    EXPECT_LE(1, gDestructions);
1589
0
    EXPECT_EQ(1, gAlive);
1590
0
    Spy::ClearActions();
1591
0
  }
1592
0
  EXPECT_EQ(1, gDestructions);
1593
0
  EXPECT_EQ(0, gAlive);
1594
0
  // Won't test xvalues anymore, prvalues are enough to verify all rvalues.
1595
0
1596
0
  Spy::ClearAll();
1597
0
  if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by const lvalue ref\n", __LINE__); }
1598
0
  { // Block around nsCOMPtr lifetime.
1599
0
    nsCOMPtr<nsIRunnable> r5;
1600
0
    { // Block around Spy lifetime.
1601
0
      if (gDebug) { printf("%d - Spy s(20)\n", __LINE__); }
1602
0
      Spy s(20);
1603
0
      EXPECT_EQ(1, gConstructions);
1604
0
      EXPECT_EQ(1, gAlive);
1605
0
      if (gDebug) { printf("%d - r5 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, s)\n", __LINE__); }
1606
0
      r5 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
1607
0
        "TestThreadUtils::ThreadUtilsObject::TestByConstLRef",
1608
0
        rpt,
1609
0
        &ThreadUtilsObject::TestByConstLRef,
1610
0
        s);
1611
0
      EXPECT_EQ(2, gAlive);
1612
0
      EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
1613
0
      Spy::ClearActions();
1614
0
      if (gDebug) { printf("%d - End block with Spy s(20)\n", __LINE__); }
1615
0
    }
1616
0
    EXPECT_EQ(1, gDestructions);
1617
0
    EXPECT_EQ(1, gAlive);
1618
0
    Spy::ClearActions();
1619
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1620
0
    r5->Run();
1621
0
    EXPECT_EQ(0, gCopyConstructions); // No copies in call.
1622
0
    EXPECT_EQ(20, rpt->mSpy.mID);
1623
0
    EXPECT_EQ(0, gDestructions);
1624
0
    EXPECT_EQ(1, gAlive);
1625
0
    Spy::ClearActions();
1626
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1627
0
  }
1628
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1629
0
  EXPECT_EQ(1, gDestructions);
1630
0
  EXPECT_EQ(0, gAlive);
1631
0
1632
0
  Spy::ClearAll();
1633
0
  if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by const lvalue ref\n", __LINE__); }
1634
0
  {
1635
0
    if (gDebug) { printf("%d - r6 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, Spy(21))\n", __LINE__); }
1636
0
    nsCOMPtr<nsIRunnable> r6 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
1637
0
      "TestThreadUtils::ThreadUtilsObject::TestByConstLRef",
1638
0
      rpt,
1639
0
      &ThreadUtilsObject::TestByConstLRef,
1640
0
      Spy(21));
1641
0
    EXPECT_EQ(1, gAlive);
1642
0
    EXPECT_EQ(1, gConstructions);
1643
0
    EXPECT_LE(1, gMoveConstructions);
1644
0
    Spy::ClearActions();
1645
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1646
0
    r6->Run();
1647
0
    EXPECT_EQ(0, gCopyConstructions); // No copies in call.
1648
0
    EXPECT_EQ(21, rpt->mSpy.mID);
1649
0
    EXPECT_EQ(0, gDestructions);
1650
0
    EXPECT_EQ(1, gAlive);
1651
0
    Spy::ClearActions();
1652
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1653
0
  }
1654
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1655
0
  EXPECT_EQ(1, gDestructions);
1656
0
  EXPECT_EQ(0, gAlive);
1657
0
1658
0
  Spy::ClearAll();
1659
0
  if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by rvalue ref\n", __LINE__); }
1660
0
  { // Block around nsCOMPtr lifetime.
1661
0
    nsCOMPtr<nsIRunnable> r7;
1662
0
    { // Block around Spy lifetime.
1663
0
      if (gDebug) { printf("%d - Spy s(30)\n", __LINE__); }
1664
0
      Spy s(30);
1665
0
      EXPECT_EQ(1, gConstructions);
1666
0
      EXPECT_EQ(1, gAlive);
1667
0
      if (gDebug) { printf("%d - r7 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(&TestByRRef, s)\n", __LINE__); }
1668
0
      r7 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
1669
0
        "TestThreadUtils::ThreadUtilsObject::TestByRRef",
1670
0
        rpt,
1671
0
        &ThreadUtilsObject::TestByRRef,
1672
0
        s);
1673
0
      EXPECT_EQ(2, gAlive);
1674
0
      EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
1675
0
      Spy::ClearActions();
1676
0
      if (gDebug) { printf("%d - End block with Spy s(30)\n", __LINE__); }
1677
0
    }
1678
0
    EXPECT_EQ(1, gDestructions);
1679
0
    EXPECT_EQ(1, gAlive);
1680
0
    Spy::ClearActions();
1681
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1682
0
    r7->Run();
1683
0
    EXPECT_LE(1, gMoves); // Move in call.
1684
0
    EXPECT_EQ(30, rpt->mSpy.mID);
1685
0
    EXPECT_EQ(0, gDestructions);
1686
0
    EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
1687
0
    EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
1688
0
    Spy::ClearActions();
1689
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1690
0
  }
1691
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1692
0
  EXPECT_EQ(1, gDestructions);
1693
0
  EXPECT_EQ(0, gAlive);
1694
0
1695
0
  Spy::ClearAll();
1696
0
  if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by rvalue ref\n", __LINE__); }
1697
0
  {
1698
0
    if (gDebug) { printf("%d - r8 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(&TestByRRef, Spy(31))\n", __LINE__); }
1699
0
    nsCOMPtr<nsIRunnable> r8 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
1700
0
      "TestThreadUtils::ThreadUtilsObject::TestByRRef",
1701
0
      rpt,
1702
0
      &ThreadUtilsObject::TestByRRef,
1703
0
      Spy(31));
1704
0
    EXPECT_EQ(1, gAlive);
1705
0
    EXPECT_EQ(1, gConstructions);
1706
0
    EXPECT_LE(1, gMoveConstructions);
1707
0
    Spy::ClearActions();
1708
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1709
0
    r8->Run();
1710
0
    EXPECT_LE(1, gMoves); // Move in call.
1711
0
    EXPECT_EQ(31, rpt->mSpy.mID);
1712
0
    EXPECT_EQ(0, gDestructions);
1713
0
    EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
1714
0
    EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
1715
0
    Spy::ClearActions();
1716
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1717
0
  }
1718
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1719
0
  EXPECT_EQ(1, gDestructions);
1720
0
  EXPECT_EQ(0, gAlive);
1721
0
1722
0
  Spy::ClearAll();
1723
0
  if (gDebug) { printf("%d - Test: Store lvalue ref, pass lvalue ref\n", __LINE__); }
1724
0
  {
1725
0
    if (gDebug) { printf("%d - Spy s(40)\n", __LINE__); }
1726
0
    Spy s(40);
1727
0
    EXPECT_EQ(1, gConstructions);
1728
0
    EXPECT_EQ(1, gAlive);
1729
0
    Spy::ClearActions();
1730
0
    if (gDebug) { printf("%d - r9 = NewRunnableMethod<Spy&>(&TestByLRef, s)\n", __LINE__); }
1731
0
    nsCOMPtr<nsIRunnable> r9 =
1732
0
      NewRunnableMethod<Spy&>("TestThreadUtils::ThreadUtilsObject::TestByLRef",
1733
0
                              rpt,
1734
0
                              &ThreadUtilsObject::TestByLRef,
1735
0
                              s);
1736
0
    EXPECT_EQ(0, gAllConstructions);
1737
0
    EXPECT_EQ(0, gDestructions);
1738
0
    EXPECT_EQ(1, gAlive);
1739
0
    Spy::ClearActions();
1740
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1741
0
    r9->Run();
1742
0
    EXPECT_LE(1, gAssignments); // Assignment from reference in call.
1743
0
    EXPECT_EQ(40, rpt->mSpy.mID);
1744
0
    EXPECT_EQ(&s, rpt->mSpyPtr);
1745
0
    EXPECT_EQ(0, gDestructions);
1746
0
    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
1747
0
    Spy::ClearActions();
1748
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1749
0
  }
1750
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1751
0
  EXPECT_EQ(1, gDestructions);
1752
0
  EXPECT_EQ(0, gAlive);
1753
0
1754
0
  Spy::ClearAll();
1755
0
  if (gDebug) { printf("%d - Test: Store nsRefPtr, pass by pointer\n", __LINE__); }
1756
0
  { // Block around nsCOMPtr lifetime.
1757
0
    nsCOMPtr<nsIRunnable> r10;
1758
0
    SpyWithISupports* ptr = 0;
1759
0
    { // Block around RefPtr<Spy> lifetime.
1760
0
      if (gDebug) { printf("%d - RefPtr<SpyWithISupports> s(new SpyWithISupports(45))\n", __LINE__); }
1761
0
      RefPtr<SpyWithISupports> s(new SpyWithISupports(45));
1762
0
      ptr = s.get();
1763
0
      EXPECT_EQ(1, gConstructions);
1764
0
      EXPECT_EQ(1, gAlive);
1765
0
      if (gDebug) { printf("%d - r10 = NewRunnableMethod<StoreRefPtrPassByPtr<Spy>>(&TestByRRef, s.get())\n", __LINE__); }
1766
0
      r10 = NewRunnableMethod<StoreRefPtrPassByPtr<SpyWithISupports>>(
1767
0
        "TestThreadUtils::ThreadUtilsObject::TestByPointer",
1768
0
        rpt,
1769
0
        &ThreadUtilsObject::TestByPointer,
1770
0
        s.get());
1771
0
      EXPECT_LE(0, gAllConstructions);
1772
0
      EXPECT_EQ(1, gAlive);
1773
0
      Spy::ClearActions();
1774
0
      if (gDebug) { printf("%d - End block with RefPtr<Spy> s\n", __LINE__); }
1775
0
    }
1776
0
    EXPECT_EQ(0, gDestructions);
1777
0
    EXPECT_EQ(1, gAlive);
1778
0
    Spy::ClearActions();
1779
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1780
0
    r10->Run();
1781
0
    EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
1782
0
    EXPECT_EQ(45, rpt->mSpy.mID);
1783
0
    EXPECT_EQ(ptr, rpt->mSpyPtr);
1784
0
    EXPECT_EQ(0, gDestructions);
1785
0
    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
1786
0
    Spy::ClearActions();
1787
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1788
0
  }
1789
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1790
0
  EXPECT_EQ(1, gDestructions);
1791
0
  EXPECT_EQ(0, gAlive);
1792
0
1793
0
  Spy::ClearAll();
1794
0
  if (gDebug) { printf("%d - Test: Store pointer to lvalue, pass by pointer\n", __LINE__); }
1795
0
  {
1796
0
    if (gDebug) { printf("%d - Spy s(55)\n", __LINE__); }
1797
0
    Spy s(55);
1798
0
    EXPECT_EQ(1, gConstructions);
1799
0
    EXPECT_EQ(1, gAlive);
1800
0
    Spy::ClearActions();
1801
0
    if (gDebug) { printf("%d - r11 = NewRunnableMethod<Spy*>(&TestByPointer, s)\n", __LINE__); }
1802
0
    nsCOMPtr<nsIRunnable> r11 = NewRunnableMethod<Spy*>(
1803
0
      "TestThreadUtils::ThreadUtilsObject::TestByPointer",
1804
0
      rpt,
1805
0
      &ThreadUtilsObject::TestByPointer,
1806
0
      &s);
1807
0
    EXPECT_EQ(0, gAllConstructions);
1808
0
    EXPECT_EQ(0, gDestructions);
1809
0
    EXPECT_EQ(1, gAlive);
1810
0
    Spy::ClearActions();
1811
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1812
0
    r11->Run();
1813
0
    EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
1814
0
    EXPECT_EQ(55, rpt->mSpy.mID);
1815
0
    EXPECT_EQ(&s, rpt->mSpyPtr);
1816
0
    EXPECT_EQ(0, gDestructions);
1817
0
    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
1818
0
    Spy::ClearActions();
1819
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1820
0
  }
1821
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1822
0
  EXPECT_EQ(1, gDestructions);
1823
0
  EXPECT_EQ(0, gAlive);
1824
0
1825
0
  Spy::ClearAll();
1826
0
  if (gDebug) { printf("%d - Test: Store pointer to const lvalue, pass by pointer\n", __LINE__); }
1827
0
  {
1828
0
    if (gDebug) { printf("%d - Spy s(60)\n", __LINE__); }
1829
0
    Spy s(60);
1830
0
    EXPECT_EQ(1, gConstructions);
1831
0
    EXPECT_EQ(1, gAlive);
1832
0
    Spy::ClearActions();
1833
0
    if (gDebug) { printf("%d - r12 = NewRunnableMethod<Spy*>(&TestByPointer, s)\n", __LINE__); }
1834
0
    nsCOMPtr<nsIRunnable> r12 = NewRunnableMethod<const Spy*>(
1835
0
      "TestThreadUtils::ThreadUtilsObject::TestByPointerToConst",
1836
0
      rpt,
1837
0
      &ThreadUtilsObject::TestByPointerToConst,
1838
0
      &s);
1839
0
    EXPECT_EQ(0, gAllConstructions);
1840
0
    EXPECT_EQ(0, gDestructions);
1841
0
    EXPECT_EQ(1, gAlive);
1842
0
    Spy::ClearActions();
1843
0
    if (gDebug) { printf("%d - Run()\n", __LINE__); }
1844
0
    r12->Run();
1845
0
    EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
1846
0
    EXPECT_EQ(60, rpt->mSpy.mID);
1847
0
    EXPECT_EQ(&s, rpt->mSpyPtr);
1848
0
    EXPECT_EQ(0, gDestructions);
1849
0
    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
1850
0
    Spy::ClearActions();
1851
0
    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
1852
0
  }
1853
0
  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
1854
0
  EXPECT_EQ(1, gDestructions);
1855
0
  EXPECT_EQ(0, gAlive);
1856
0
}