/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(©Counter); |
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(©Counter)) |
210 | 0 | : NS_NewRunnableFunction("TestNewRunnableFunction", |
211 | 0 | TestCopyWithNoMove(©Counter)); |
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(©Counter); |
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(©Counter, &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(©Counter, &moveCounter)) |
297 | 0 | : NS_NewRunnableFunction("TestNewRunnableFunction", |
298 | 0 | TestCopyMove(©Counter, &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(©Counter); |
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(©Counter); |
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(©Counter, &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 | } |