OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 } | 130 } |
131 | 131 |
132 virtual void Run() { | 132 virtual void Run() { |
133 // Do nothing. | 133 // Do nothing. |
134 } | 134 } |
135 | 135 |
136 private: | 136 private: |
137 Event* event_; | 137 Event* event_; |
138 }; | 138 }; |
139 | 139 |
| 140 // A bool wrapped in a mutex, to avoid data races. Using a volatile |
| 141 // bool should be sufficient for correct code ("eventual consistency" |
| 142 // between caches is sufficient), but we can't tell the compiler about |
| 143 // that, and then tsan complains about a data race. |
| 144 |
| 145 // See also discussion at |
| 146 // http://stackoverflow.com/questions/7223164/is-mutex-needed-to-synchronize-a-s
imple-flag-between-pthreads |
| 147 |
| 148 // Using std::atomic<bool> or std::atomic_flag in C++11 is probably |
| 149 // the right thing to do, but those features are not yet allowed. Or |
| 150 // rtc::AtomicInt, if/when that is added. Since the use isn't |
| 151 // performance critical, use a plain critical section for the time |
| 152 // being. |
| 153 |
| 154 class AtomicBool { |
| 155 public: |
| 156 explicit AtomicBool(bool value = false) : flag_(value) {} |
| 157 AtomicBool& operator=(bool value) { |
| 158 CritScope scoped_lock(&cs_); |
| 159 flag_ = value; |
| 160 return *this; |
| 161 } |
| 162 bool get() const { |
| 163 CritScope scoped_lock(&cs_); |
| 164 return flag_; |
| 165 } |
| 166 |
| 167 private: |
| 168 mutable CriticalSection cs_; |
| 169 bool flag_; |
| 170 }; |
| 171 |
140 // Function objects to test Thread::Invoke. | 172 // Function objects to test Thread::Invoke. |
141 struct FunctorA { | 173 struct FunctorA { |
142 int operator()() { return 42; } | 174 int operator()() { return 42; } |
143 }; | 175 }; |
144 class FunctorB { | 176 class FunctorB { |
145 public: | 177 public: |
146 explicit FunctorB(bool* flag) : flag_(flag) {} | 178 explicit FunctorB(AtomicBool* flag) : flag_(flag) {} |
147 void operator()() { if (flag_) *flag_ = true; } | 179 void operator()() { if (flag_) *flag_ = true; } |
148 private: | 180 private: |
149 bool* flag_; | 181 AtomicBool* flag_; |
150 }; | 182 }; |
151 struct FunctorC { | 183 struct FunctorC { |
152 int operator()() { | 184 int operator()() { |
153 Thread::Current()->ProcessMessages(50); | 185 Thread::Current()->ProcessMessages(50); |
154 return 24; | 186 return 24; |
155 } | 187 } |
156 }; | 188 }; |
157 | 189 |
158 // See: https://code.google.com/p/webrtc/issues/detail?id=2409 | 190 // See: https://code.google.com/p/webrtc/issues/detail?id=2409 |
159 TEST(ThreadTest, DISABLED_Main) { | 191 TEST(ThreadTest, DISABLED_Main) { |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 delete cthread; | 291 delete cthread; |
260 current_thread->WrapCurrent(); | 292 current_thread->WrapCurrent(); |
261 } | 293 } |
262 | 294 |
263 TEST(ThreadTest, Invoke) { | 295 TEST(ThreadTest, Invoke) { |
264 // Create and start the thread. | 296 // Create and start the thread. |
265 Thread thread; | 297 Thread thread; |
266 thread.Start(); | 298 thread.Start(); |
267 // Try calling functors. | 299 // Try calling functors. |
268 EXPECT_EQ(42, thread.Invoke<int>(FunctorA())); | 300 EXPECT_EQ(42, thread.Invoke<int>(FunctorA())); |
269 bool called = false; | 301 AtomicBool called; |
270 FunctorB f2(&called); | 302 FunctorB f2(&called); |
271 thread.Invoke<void>(f2); | 303 thread.Invoke<void>(f2); |
272 EXPECT_TRUE(called); | 304 EXPECT_TRUE(called.get()); |
273 // Try calling bare functions. | 305 // Try calling bare functions. |
274 struct LocalFuncs { | 306 struct LocalFuncs { |
275 static int Func1() { return 999; } | 307 static int Func1() { return 999; } |
276 static void Func2() {} | 308 static void Func2() {} |
277 }; | 309 }; |
278 EXPECT_EQ(999, thread.Invoke<int>(&LocalFuncs::Func1)); | 310 EXPECT_EQ(999, thread.Invoke<int>(&LocalFuncs::Func1)); |
279 thread.Invoke<void>(&LocalFuncs::Func2); | 311 thread.Invoke<void>(&LocalFuncs::Func2); |
280 } | 312 } |
281 | 313 |
282 // Verifies that two threads calling Invoke on each other at the same time does | 314 // Verifies that two threads calling Invoke on each other at the same time does |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 Event invoke_started_; | 433 Event invoke_started_; |
402 Thread* expected_thread_; | 434 Thread* expected_thread_; |
403 }; | 435 }; |
404 | 436 |
405 TEST_F(AsyncInvokeTest, FireAndForget) { | 437 TEST_F(AsyncInvokeTest, FireAndForget) { |
406 AsyncInvoker invoker; | 438 AsyncInvoker invoker; |
407 // Create and start the thread. | 439 // Create and start the thread. |
408 Thread thread; | 440 Thread thread; |
409 thread.Start(); | 441 thread.Start(); |
410 // Try calling functor. | 442 // Try calling functor. |
411 bool called = false; | 443 AtomicBool called; |
412 invoker.AsyncInvoke<void>(&thread, FunctorB(&called)); | 444 invoker.AsyncInvoke<void>(&thread, FunctorB(&called)); |
413 EXPECT_TRUE_WAIT(called, kWaitTimeout); | 445 EXPECT_TRUE_WAIT(called.get(), kWaitTimeout); |
414 } | 446 } |
415 | 447 |
416 TEST_F(AsyncInvokeTest, WithCallback) { | 448 TEST_F(AsyncInvokeTest, WithCallback) { |
417 AsyncInvoker invoker; | 449 AsyncInvoker invoker; |
418 // Create and start the thread. | 450 // Create and start the thread. |
419 Thread thread; | 451 Thread thread; |
420 thread.Start(); | 452 thread.Start(); |
421 // Try calling functor. | 453 // Try calling functor. |
422 SetExpectedThreadForIntCallback(Thread::Current()); | 454 SetExpectedThreadForIntCallback(Thread::Current()); |
423 invoker.AsyncInvoke(&thread, FunctorA(), | 455 invoker.AsyncInvoke(&thread, FunctorA(), |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
471 // Wait for the call to begin. | 503 // Wait for the call to begin. |
472 ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); | 504 ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); |
473 } | 505 } |
474 // Invoker is destroyed. Function should not execute. | 506 // Invoker is destroyed. Function should not execute. |
475 Thread::Current()->ProcessMessages(kWaitTimeout); | 507 Thread::Current()->ProcessMessages(kWaitTimeout); |
476 EXPECT_EQ(0, int_value_); | 508 EXPECT_EQ(0, int_value_); |
477 } | 509 } |
478 | 510 |
479 TEST_F(AsyncInvokeTest, Flush) { | 511 TEST_F(AsyncInvokeTest, Flush) { |
480 AsyncInvoker invoker; | 512 AsyncInvoker invoker; |
481 bool flag1 = false; | 513 AtomicBool flag1; |
482 bool flag2 = false; | 514 AtomicBool flag2; |
483 // Queue two async calls to the current thread. | 515 // Queue two async calls to the current thread. |
484 invoker.AsyncInvoke<void>(Thread::Current(), | 516 invoker.AsyncInvoke<void>(Thread::Current(), |
485 FunctorB(&flag1)); | 517 FunctorB(&flag1)); |
486 invoker.AsyncInvoke<void>(Thread::Current(), | 518 invoker.AsyncInvoke<void>(Thread::Current(), |
487 FunctorB(&flag2)); | 519 FunctorB(&flag2)); |
488 // Because we haven't pumped messages, these should not have run yet. | 520 // Because we haven't pumped messages, these should not have run yet. |
489 EXPECT_FALSE(flag1); | 521 EXPECT_FALSE(flag1.get()); |
490 EXPECT_FALSE(flag2); | 522 EXPECT_FALSE(flag2.get()); |
491 // Force them to run now. | 523 // Force them to run now. |
492 invoker.Flush(Thread::Current()); | 524 invoker.Flush(Thread::Current()); |
493 EXPECT_TRUE(flag1); | 525 EXPECT_TRUE(flag1.get()); |
494 EXPECT_TRUE(flag2); | 526 EXPECT_TRUE(flag2.get()); |
495 } | 527 } |
496 | 528 |
497 TEST_F(AsyncInvokeTest, FlushWithIds) { | 529 TEST_F(AsyncInvokeTest, FlushWithIds) { |
498 AsyncInvoker invoker; | 530 AsyncInvoker invoker; |
499 bool flag1 = false; | 531 AtomicBool flag1; |
500 bool flag2 = false; | 532 AtomicBool flag2; |
501 // Queue two async calls to the current thread, one with a message id. | 533 // Queue two async calls to the current thread, one with a message id. |
502 invoker.AsyncInvoke<void>(Thread::Current(), | 534 invoker.AsyncInvoke<void>(Thread::Current(), |
503 FunctorB(&flag1), | 535 FunctorB(&flag1), |
504 5); | 536 5); |
505 invoker.AsyncInvoke<void>(Thread::Current(), | 537 invoker.AsyncInvoke<void>(Thread::Current(), |
506 FunctorB(&flag2)); | 538 FunctorB(&flag2)); |
507 // Because we haven't pumped messages, these should not have run yet. | 539 // Because we haven't pumped messages, these should not have run yet. |
508 EXPECT_FALSE(flag1); | 540 EXPECT_FALSE(flag1.get()); |
509 EXPECT_FALSE(flag2); | 541 EXPECT_FALSE(flag2.get()); |
510 // Execute pending calls with id == 5. | 542 // Execute pending calls with id == 5. |
511 invoker.Flush(Thread::Current(), 5); | 543 invoker.Flush(Thread::Current(), 5); |
512 EXPECT_TRUE(flag1); | 544 EXPECT_TRUE(flag1.get()); |
513 EXPECT_FALSE(flag2); | 545 EXPECT_FALSE(flag2.get()); |
514 flag1 = false; | 546 flag1 = false; |
515 // Execute all pending calls. The id == 5 call should not execute again. | 547 // Execute all pending calls. The id == 5 call should not execute again. |
516 invoker.Flush(Thread::Current()); | 548 invoker.Flush(Thread::Current()); |
517 EXPECT_FALSE(flag1); | 549 EXPECT_FALSE(flag1.get()); |
518 EXPECT_TRUE(flag2); | 550 EXPECT_TRUE(flag2.get()); |
519 } | 551 } |
520 | 552 |
521 class GuardedAsyncInvokeTest : public testing::Test { | 553 class GuardedAsyncInvokeTest : public testing::Test { |
522 public: | 554 public: |
523 void IntCallback(int value) { | 555 void IntCallback(int value) { |
524 EXPECT_EQ(expected_thread_, Thread::Current()); | 556 EXPECT_EQ(expected_thread_, Thread::Current()); |
525 int_value_ = value; | 557 int_value_ = value; |
526 } | 558 } |
527 void AsyncInvokeIntCallback(GuardedAsyncInvoker* invoker, Thread* thread) { | 559 void AsyncInvokeIntCallback(GuardedAsyncInvoker* invoker, Thread* thread) { |
528 expected_thread_ = thread; | 560 expected_thread_ = thread; |
(...skipping 28 matching lines...) Expand all Loading... |
557 TEST_F(GuardedAsyncInvokeTest, KillThreadFireAndForget) { | 589 TEST_F(GuardedAsyncInvokeTest, KillThreadFireAndForget) { |
558 // Create and start the thread. | 590 // Create and start the thread. |
559 scoped_ptr<Thread> thread(new Thread()); | 591 scoped_ptr<Thread> thread(new Thread()); |
560 thread->Start(); | 592 thread->Start(); |
561 scoped_ptr<GuardedAsyncInvoker> invoker; | 593 scoped_ptr<GuardedAsyncInvoker> invoker; |
562 // Create the invoker on |thread|. | 594 // Create the invoker on |thread|. |
563 thread->Invoke<void>(CreateInvoker(&invoker)); | 595 thread->Invoke<void>(CreateInvoker(&invoker)); |
564 // Kill |thread|. | 596 // Kill |thread|. |
565 thread = nullptr; | 597 thread = nullptr; |
566 // Try calling functor. | 598 // Try calling functor. |
567 bool called = false; | 599 AtomicBool called; |
568 EXPECT_FALSE(invoker->AsyncInvoke<void>(FunctorB(&called))); | 600 EXPECT_FALSE(invoker->AsyncInvoke<void>(FunctorB(&called))); |
569 // With thread gone, nothing should happen. | 601 // With thread gone, nothing should happen. |
570 WAIT(called, kWaitTimeout); | 602 WAIT(called.get(), kWaitTimeout); |
571 EXPECT_FALSE(called); | 603 EXPECT_FALSE(called.get()); |
572 } | 604 } |
573 | 605 |
574 // Test that we can call AsyncInvoke with callback after the thread died. | 606 // Test that we can call AsyncInvoke with callback after the thread died. |
575 TEST_F(GuardedAsyncInvokeTest, KillThreadWithCallback) { | 607 TEST_F(GuardedAsyncInvokeTest, KillThreadWithCallback) { |
576 // Create and start the thread. | 608 // Create and start the thread. |
577 scoped_ptr<Thread> thread(new Thread()); | 609 scoped_ptr<Thread> thread(new Thread()); |
578 thread->Start(); | 610 thread->Start(); |
579 scoped_ptr<GuardedAsyncInvoker> invoker; | 611 scoped_ptr<GuardedAsyncInvoker> invoker; |
580 // Create the invoker on |thread|. | 612 // Create the invoker on |thread|. |
581 thread->Invoke<void>(CreateInvoker(&invoker)); | 613 thread->Invoke<void>(CreateInvoker(&invoker)); |
582 // Kill |thread|. | 614 // Kill |thread|. |
583 thread = nullptr; | 615 thread = nullptr; |
584 // Try calling functor. | 616 // Try calling functor. |
585 EXPECT_FALSE( | 617 EXPECT_FALSE( |
586 invoker->AsyncInvoke(FunctorC(), &GuardedAsyncInvokeTest::IntCallback, | 618 invoker->AsyncInvoke(FunctorC(), &GuardedAsyncInvokeTest::IntCallback, |
587 static_cast<GuardedAsyncInvokeTest*>(this))); | 619 static_cast<GuardedAsyncInvokeTest*>(this))); |
588 // With thread gone, callback should be cancelled. | 620 // With thread gone, callback should be cancelled. |
589 Thread::Current()->ProcessMessages(kWaitTimeout); | 621 Thread::Current()->ProcessMessages(kWaitTimeout); |
590 EXPECT_EQ(0, int_value_); | 622 EXPECT_EQ(0, int_value_); |
591 } | 623 } |
592 | 624 |
593 // The remaining tests check that GuardedAsyncInvoker behaves as AsyncInvoker | 625 // The remaining tests check that GuardedAsyncInvoker behaves as AsyncInvoker |
594 // when Thread is still alive. | 626 // when Thread is still alive. |
595 TEST_F(GuardedAsyncInvokeTest, FireAndForget) { | 627 TEST_F(GuardedAsyncInvokeTest, FireAndForget) { |
596 GuardedAsyncInvoker invoker; | 628 GuardedAsyncInvoker invoker; |
597 // Try calling functor. | 629 // Try calling functor. |
598 bool called = false; | 630 AtomicBool called; |
599 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&called))); | 631 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&called))); |
600 EXPECT_TRUE_WAIT(called, kWaitTimeout); | 632 EXPECT_TRUE_WAIT(called.get(), kWaitTimeout); |
601 } | 633 } |
602 | 634 |
603 TEST_F(GuardedAsyncInvokeTest, WithCallback) { | 635 TEST_F(GuardedAsyncInvokeTest, WithCallback) { |
604 GuardedAsyncInvoker invoker; | 636 GuardedAsyncInvoker invoker; |
605 // Try calling functor. | 637 // Try calling functor. |
606 SetExpectedThreadForIntCallback(Thread::Current()); | 638 SetExpectedThreadForIntCallback(Thread::Current()); |
607 EXPECT_TRUE(invoker.AsyncInvoke(FunctorA(), | 639 EXPECT_TRUE(invoker.AsyncInvoke(FunctorA(), |
608 &GuardedAsyncInvokeTest::IntCallback, | 640 &GuardedAsyncInvokeTest::IntCallback, |
609 static_cast<GuardedAsyncInvokeTest*>(this))); | 641 static_cast<GuardedAsyncInvokeTest*>(this))); |
610 EXPECT_EQ_WAIT(42, int_value_, kWaitTimeout); | 642 EXPECT_EQ_WAIT(42, int_value_, kWaitTimeout); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
653 // Wait for the call to begin. | 685 // Wait for the call to begin. |
654 ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); | 686 ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); |
655 } | 687 } |
656 // Invoker is destroyed. Function should not execute. | 688 // Invoker is destroyed. Function should not execute. |
657 Thread::Current()->ProcessMessages(kWaitTimeout); | 689 Thread::Current()->ProcessMessages(kWaitTimeout); |
658 EXPECT_EQ(0, int_value_); | 690 EXPECT_EQ(0, int_value_); |
659 } | 691 } |
660 | 692 |
661 TEST_F(GuardedAsyncInvokeTest, Flush) { | 693 TEST_F(GuardedAsyncInvokeTest, Flush) { |
662 GuardedAsyncInvoker invoker; | 694 GuardedAsyncInvoker invoker; |
663 bool flag1 = false; | 695 AtomicBool flag1; |
664 bool flag2 = false; | 696 AtomicBool flag2; |
665 // Queue two async calls to the current thread. | 697 // Queue two async calls to the current thread. |
666 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag1))); | 698 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag1))); |
667 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag2))); | 699 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag2))); |
668 // Because we haven't pumped messages, these should not have run yet. | 700 // Because we haven't pumped messages, these should not have run yet. |
669 EXPECT_FALSE(flag1); | 701 EXPECT_FALSE(flag1.get()); |
670 EXPECT_FALSE(flag2); | 702 EXPECT_FALSE(flag2.get()); |
671 // Force them to run now. | 703 // Force them to run now. |
672 EXPECT_TRUE(invoker.Flush()); | 704 EXPECT_TRUE(invoker.Flush()); |
673 EXPECT_TRUE(flag1); | 705 EXPECT_TRUE(flag1.get()); |
674 EXPECT_TRUE(flag2); | 706 EXPECT_TRUE(flag2.get()); |
675 } | 707 } |
676 | 708 |
677 TEST_F(GuardedAsyncInvokeTest, FlushWithIds) { | 709 TEST_F(GuardedAsyncInvokeTest, FlushWithIds) { |
678 GuardedAsyncInvoker invoker; | 710 GuardedAsyncInvoker invoker; |
679 bool flag1 = false; | 711 AtomicBool flag1; |
680 bool flag2 = false; | 712 AtomicBool flag2; |
681 // Queue two async calls to the current thread, one with a message id. | 713 // Queue two async calls to the current thread, one with a message id. |
682 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag1), 5)); | 714 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag1), 5)); |
683 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag2))); | 715 EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag2))); |
684 // Because we haven't pumped messages, these should not have run yet. | 716 // Because we haven't pumped messages, these should not have run yet. |
685 EXPECT_FALSE(flag1); | 717 EXPECT_FALSE(flag1.get()); |
686 EXPECT_FALSE(flag2); | 718 EXPECT_FALSE(flag2.get()); |
687 // Execute pending calls with id == 5. | 719 // Execute pending calls with id == 5. |
688 EXPECT_TRUE(invoker.Flush(5)); | 720 EXPECT_TRUE(invoker.Flush(5)); |
689 EXPECT_TRUE(flag1); | 721 EXPECT_TRUE(flag1.get()); |
690 EXPECT_FALSE(flag2); | 722 EXPECT_FALSE(flag2.get()); |
691 flag1 = false; | 723 flag1 = false; |
692 // Execute all pending calls. The id == 5 call should not execute again. | 724 // Execute all pending calls. The id == 5 call should not execute again. |
693 EXPECT_TRUE(invoker.Flush()); | 725 EXPECT_TRUE(invoker.Flush()); |
694 EXPECT_FALSE(flag1); | 726 EXPECT_FALSE(flag1.get()); |
695 EXPECT_TRUE(flag2); | 727 EXPECT_TRUE(flag2.get()); |
696 } | 728 } |
697 | 729 |
698 #if defined(WEBRTC_WIN) | 730 #if defined(WEBRTC_WIN) |
699 class ComThreadTest : public testing::Test, public MessageHandler { | 731 class ComThreadTest : public testing::Test, public MessageHandler { |
700 public: | 732 public: |
701 ComThreadTest() : done_(false) {} | 733 ComThreadTest() : done_(false) {} |
702 protected: | 734 protected: |
703 virtual void OnMessage(Message* message) { | 735 virtual void OnMessage(Message* message) { |
704 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); | 736 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); |
705 // S_FALSE means the thread was already inited for a multithread apartment. | 737 // S_FALSE means the thread was already inited for a multithread apartment. |
706 EXPECT_EQ(S_FALSE, hr); | 738 EXPECT_EQ(S_FALSE, hr); |
707 if (SUCCEEDED(hr)) { | 739 if (SUCCEEDED(hr)) { |
708 CoUninitialize(); | 740 CoUninitialize(); |
709 } | 741 } |
710 done_ = true; | 742 done_ = true; |
711 } | 743 } |
712 bool done_; | 744 bool done_; |
713 }; | 745 }; |
714 | 746 |
715 TEST_F(ComThreadTest, ComInited) { | 747 TEST_F(ComThreadTest, ComInited) { |
716 Thread* thread = new ComThread(); | 748 Thread* thread = new ComThread(); |
717 EXPECT_TRUE(thread->Start()); | 749 EXPECT_TRUE(thread->Start()); |
718 thread->Post(this, 0); | 750 thread->Post(this, 0); |
719 EXPECT_TRUE_WAIT(done_, 1000); | 751 EXPECT_TRUE_WAIT(done_, 1000); |
720 delete thread; | 752 delete thread; |
721 } | 753 } |
722 #endif | 754 #endif |
OLD | NEW |