1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/threading/thread.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/debug/leak_annotations.h"
15 #include "base/macros.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/run_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/synchronization/waitable_event.h"
20 #include "base/task/post_task.h"
21 #include "base/task/sequence_manager/sequence_manager_impl.h"
22 #include "base/task/task_executor.h"
23 #include "base/test/bind_test_util.h"
24 #include "base/test/gtest_util.h"
25 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
26 #include "base/threading/platform_thread.h"
27 #include "base/time/time.h"
28 #include "build/build_config.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "testing/platform_test.h"
32
33 using base::Thread;
34 using ::testing::NotNull;
35
36 typedef PlatformTest ThreadTest;
37
38 namespace {
39
ToggleValue(bool * value)40 void ToggleValue(bool* value) {
41 ANNOTATE_BENIGN_RACE(value, "Test-only data race on boolean "
42 "in base/thread_unittest");
43 *value = !*value;
44 }
45
46 class SleepInsideInitThread : public Thread {
47 public:
SleepInsideInitThread()48 SleepInsideInitThread() : Thread("none") {
49 init_called_ = false;
50 ANNOTATE_BENIGN_RACE(
51 this, "Benign test-only data race on vptr - http://crbug.com/98219");
52 }
~SleepInsideInitThread()53 ~SleepInsideInitThread() override { Stop(); }
54
Init()55 void Init() override {
56 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
57 init_called_ = true;
58 }
InitCalled()59 bool InitCalled() { return init_called_; }
60
61 private:
62 bool init_called_;
63
64 DISALLOW_COPY_AND_ASSIGN(SleepInsideInitThread);
65 };
66
67 enum ThreadEvent {
68 // Thread::Init() was called.
69 THREAD_EVENT_INIT = 0,
70
71 // The MessageLoop for the thread was deleted.
72 THREAD_EVENT_MESSAGE_LOOP_DESTROYED,
73
74 // Thread::CleanUp() was called.
75 THREAD_EVENT_CLEANUP,
76
77 // Keep at end of list.
78 THREAD_NUM_EVENTS
79 };
80
81 typedef std::vector<ThreadEvent> EventList;
82
83 class CaptureToEventList : public Thread {
84 public:
85 // This Thread pushes events into the vector |event_list| to show
86 // the order they occured in. |event_list| must remain valid for the
87 // lifetime of this thread.
CaptureToEventList(EventList * event_list)88 explicit CaptureToEventList(EventList* event_list)
89 : Thread("none"),
90 event_list_(event_list) {
91 }
92
~CaptureToEventList()93 ~CaptureToEventList() override { Stop(); }
94
Init()95 void Init() override { event_list_->push_back(THREAD_EVENT_INIT); }
96
CleanUp()97 void CleanUp() override { event_list_->push_back(THREAD_EVENT_CLEANUP); }
98
99 private:
100 EventList* event_list_;
101
102 DISALLOW_COPY_AND_ASSIGN(CaptureToEventList);
103 };
104
105 // Observer that writes a value into |event_list| when a message loop has been
106 // destroyed.
107 class CapturingDestructionObserver
108 : public base::MessageLoopCurrent::DestructionObserver {
109 public:
110 // |event_list| must remain valid throughout the observer's lifetime.
CapturingDestructionObserver(EventList * event_list)111 explicit CapturingDestructionObserver(EventList* event_list)
112 : event_list_(event_list) {
113 }
114
115 // DestructionObserver implementation:
WillDestroyCurrentMessageLoop()116 void WillDestroyCurrentMessageLoop() override {
117 event_list_->push_back(THREAD_EVENT_MESSAGE_LOOP_DESTROYED);
118 event_list_ = nullptr;
119 }
120
121 private:
122 EventList* event_list_;
123
124 DISALLOW_COPY_AND_ASSIGN(CapturingDestructionObserver);
125 };
126
127 // Task that adds a destruction observer to the current message loop.
RegisterDestructionObserver(base::MessageLoopCurrent::DestructionObserver * observer)128 void RegisterDestructionObserver(
129 base::MessageLoopCurrent::DestructionObserver* observer) {
130 base::MessageLoopCurrent::Get()->AddDestructionObserver(observer);
131 }
132
133 // Task that calls GetThreadId() of |thread|, stores the result into |id|, then
134 // signal |event|.
ReturnThreadId(base::Thread * thread,base::PlatformThreadId * id,base::WaitableEvent * event)135 void ReturnThreadId(base::Thread* thread,
136 base::PlatformThreadId* id,
137 base::WaitableEvent* event) {
138 *id = thread->GetThreadId();
139 event->Signal();
140 }
141
142 } // namespace
143
TEST_F(ThreadTest,StartWithOptions_StackSize)144 TEST_F(ThreadTest, StartWithOptions_StackSize) {
145 Thread a("StartWithStackSize");
146 // Ensure that the thread can work with only 12 kb and still process a
147 // message. At the same time, we should scale with the bitness of the system
148 // where 12 kb is definitely not enough.
149 // 12 kb = 3072 Slots on a 32-bit system, so we'll scale based off of that.
150 Thread::Options options;
151 #if defined(ADDRESS_SANITIZER) || !defined(NDEBUG)
152 // ASan bloats the stack variables and overflows the 3072 slot stack. Some
153 // debug builds also grow the stack too much.
154 options.stack_size = 2 * 3072 * sizeof(uintptr_t);
155 #else
156 options.stack_size = 3072 * sizeof(uintptr_t);
157 #endif
158 EXPECT_TRUE(a.StartWithOptions(options));
159 EXPECT_TRUE(a.task_runner());
160 EXPECT_TRUE(a.IsRunning());
161
162 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
163 base::WaitableEvent::InitialState::NOT_SIGNALED);
164 a.task_runner()->PostTask(
165 FROM_HERE,
166 base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event)));
167 event.Wait();
168 }
169
170 // Intentional test-only race for otherwise untestable code, won't fix.
171 // https://crbug.com/634383
172 #if !defined(THREAD_SANITIZER)
TEST_F(ThreadTest,StartWithOptions_NonJoinable)173 TEST_F(ThreadTest, StartWithOptions_NonJoinable) {
174 Thread* a = new Thread("StartNonJoinable");
175 // Non-joinable threads have to be leaked for now (see
176 // Thread::Options::joinable for details).
177 ANNOTATE_LEAKING_OBJECT_PTR(a);
178
179 Thread::Options options;
180 options.joinable = false;
181 EXPECT_TRUE(a->StartWithOptions(options));
182 EXPECT_TRUE(a->task_runner());
183 EXPECT_TRUE(a->IsRunning());
184
185 // Without this call this test is racy. The above IsRunning() succeeds because
186 // of an early-return condition while between Start() and StopSoon(), after
187 // invoking StopSoon() below this early-return condition is no longer
188 // satisfied and the real |is_running_| bit has to be checked. It could still
189 // be false if the message loop hasn't started for real in practice. This is
190 // only a requirement for this test because the non-joinable property forces
191 // it to use StopSoon() and not wait for a complete Stop().
192 EXPECT_TRUE(a->WaitUntilThreadStarted());
193
194 // Make the thread block until |block_event| is signaled.
195 base::WaitableEvent block_event(
196 base::WaitableEvent::ResetPolicy::AUTOMATIC,
197 base::WaitableEvent::InitialState::NOT_SIGNALED);
198 a->task_runner()->PostTask(FROM_HERE,
199 base::BindOnce(&base::WaitableEvent::Wait,
200 base::Unretained(&block_event)));
201
202 a->StopSoon();
203 EXPECT_TRUE(a->IsRunning());
204
205 // Unblock the task and give a bit of extra time to unwind QuitWhenIdle().
206 block_event.Signal();
207 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
208
209 // The thread should now have stopped on its own.
210 EXPECT_FALSE(a->IsRunning());
211 }
212 #endif
213
TEST_F(ThreadTest,TwoTasksOnJoinableThread)214 TEST_F(ThreadTest, TwoTasksOnJoinableThread) {
215 bool was_invoked = false;
216 {
217 Thread a("TwoTasksOnJoinableThread");
218 EXPECT_TRUE(a.Start());
219 EXPECT_TRUE(a.task_runner());
220
221 // Test that all events are dispatched before the Thread object is
222 // destroyed. We do this by dispatching a sleep event before the
223 // event that will toggle our sentinel value.
224 a.task_runner()->PostTask(
225 FROM_HERE, base::BindOnce(static_cast<void (*)(base::TimeDelta)>(
226 &base::PlatformThread::Sleep),
227 base::TimeDelta::FromMilliseconds(20)));
228 a.task_runner()->PostTask(FROM_HERE,
229 base::BindOnce(&ToggleValue, &was_invoked));
230 }
231 EXPECT_TRUE(was_invoked);
232 }
233
TEST_F(ThreadTest,DestroyWhileRunningIsSafe)234 TEST_F(ThreadTest, DestroyWhileRunningIsSafe) {
235 Thread a("DestroyWhileRunningIsSafe");
236 EXPECT_TRUE(a.Start());
237 EXPECT_TRUE(a.WaitUntilThreadStarted());
238 }
239
240 // TODO(gab): Enable this test when destroying a non-joinable Thread instance
241 // is supported (proposal @ https://crbug.com/629139#c14).
TEST_F(ThreadTest,DISABLED_DestroyWhileRunningNonJoinableIsSafe)242 TEST_F(ThreadTest, DISABLED_DestroyWhileRunningNonJoinableIsSafe) {
243 {
244 Thread a("DestroyWhileRunningNonJoinableIsSafe");
245 Thread::Options options;
246 options.joinable = false;
247 EXPECT_TRUE(a.StartWithOptions(options));
248 EXPECT_TRUE(a.WaitUntilThreadStarted());
249 }
250
251 // Attempt to catch use-after-frees from the non-joinable thread in the
252 // scope of this test if any.
253 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
254 }
255
TEST_F(ThreadTest,StopSoon)256 TEST_F(ThreadTest, StopSoon) {
257 Thread a("StopSoon");
258 EXPECT_TRUE(a.Start());
259 EXPECT_TRUE(a.task_runner());
260 EXPECT_TRUE(a.IsRunning());
261 a.StopSoon();
262 a.Stop();
263 EXPECT_FALSE(a.task_runner());
264 EXPECT_FALSE(a.IsRunning());
265 }
266
TEST_F(ThreadTest,StopTwiceNop)267 TEST_F(ThreadTest, StopTwiceNop) {
268 Thread a("StopTwiceNop");
269 EXPECT_TRUE(a.Start());
270 EXPECT_TRUE(a.task_runner());
271 EXPECT_TRUE(a.IsRunning());
272 a.StopSoon();
273 // Calling StopSoon() a second time should be a nop.
274 a.StopSoon();
275 a.Stop();
276 // Same with Stop().
277 a.Stop();
278 EXPECT_FALSE(a.task_runner());
279 EXPECT_FALSE(a.IsRunning());
280 // Calling them when not running should also nop.
281 a.StopSoon();
282 a.Stop();
283 }
284
285 // TODO(gab): Enable this test in conjunction with re-enabling the sequence
286 // check in Thread::Stop() as part of http://crbug.com/629139.
TEST_F(ThreadTest,DISABLED_StopOnNonOwningThreadIsDeath)287 TEST_F(ThreadTest, DISABLED_StopOnNonOwningThreadIsDeath) {
288 Thread a("StopOnNonOwningThreadDeath");
289 EXPECT_TRUE(a.StartAndWaitForTesting());
290
291 Thread b("NonOwningThread");
292 b.Start();
293 EXPECT_DCHECK_DEATH({
294 // Stopping |a| on |b| isn't allowed.
295 b.task_runner()->PostTask(
296 FROM_HERE, base::BindOnce(&Thread::Stop, base::Unretained(&a)));
297 // Block here so the DCHECK on |b| always happens in this scope.
298 base::PlatformThread::Sleep(base::TimeDelta::Max());
299 });
300 }
301
TEST_F(ThreadTest,TransferOwnershipAndStop)302 TEST_F(ThreadTest, TransferOwnershipAndStop) {
303 std::unique_ptr<Thread> a =
304 std::make_unique<Thread>("TransferOwnershipAndStop");
305 EXPECT_TRUE(a->StartAndWaitForTesting());
306 EXPECT_TRUE(a->IsRunning());
307
308 Thread b("TakingOwnershipThread");
309 b.Start();
310
311 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
312 base::WaitableEvent::InitialState::NOT_SIGNALED);
313
314 // a->DetachFromSequence() should allow |b| to use |a|'s Thread API.
315 a->DetachFromSequence();
316 b.task_runner()->PostTask(
317 FROM_HERE, base::BindOnce(
318 [](std::unique_ptr<Thread> thread_to_stop,
319 base::WaitableEvent* event_to_signal) -> void {
320 thread_to_stop->Stop();
321 event_to_signal->Signal();
322 },
323 std::move(a), base::Unretained(&event)));
324
325 event.Wait();
326 }
327
TEST_F(ThreadTest,StartTwice)328 TEST_F(ThreadTest, StartTwice) {
329 Thread a("StartTwice");
330
331 EXPECT_FALSE(a.task_runner());
332 EXPECT_FALSE(a.IsRunning());
333
334 EXPECT_TRUE(a.Start());
335 EXPECT_TRUE(a.task_runner());
336 EXPECT_TRUE(a.IsRunning());
337
338 a.Stop();
339 EXPECT_FALSE(a.task_runner());
340 EXPECT_FALSE(a.IsRunning());
341
342 EXPECT_TRUE(a.Start());
343 EXPECT_TRUE(a.task_runner());
344 EXPECT_TRUE(a.IsRunning());
345
346 a.Stop();
347 EXPECT_FALSE(a.task_runner());
348 EXPECT_FALSE(a.IsRunning());
349 }
350
351 // Intentional test-only race for otherwise untestable code, won't fix.
352 // https://crbug.com/634383
353 #if !defined(THREAD_SANITIZER)
TEST_F(ThreadTest,StartTwiceNonJoinableNotAllowed)354 TEST_F(ThreadTest, StartTwiceNonJoinableNotAllowed) {
355 LOG(ERROR) << __FUNCTION__;
356 Thread* a = new Thread("StartTwiceNonJoinable");
357 // Non-joinable threads have to be leaked for now (see
358 // Thread::Options::joinable for details).
359 ANNOTATE_LEAKING_OBJECT_PTR(a);
360
361 Thread::Options options;
362 options.joinable = false;
363 EXPECT_TRUE(a->StartWithOptions(options));
364 EXPECT_TRUE(a->task_runner());
365 EXPECT_TRUE(a->IsRunning());
366
367 // Signaled when last task on |a| is processed.
368 base::WaitableEvent last_task_event(
369 base::WaitableEvent::ResetPolicy::AUTOMATIC,
370 base::WaitableEvent::InitialState::NOT_SIGNALED);
371 a->task_runner()->PostTask(
372 FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
373 base::Unretained(&last_task_event)));
374
375 // StopSoon() is non-blocking, Yield() to |a|, wait for last task to be
376 // processed and a little more for QuitWhenIdle() to unwind before considering
377 // the thread "stopped".
378 a->StopSoon();
379 base::PlatformThread::YieldCurrentThread();
380 last_task_event.Wait();
381 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
382
383 // This test assumes that the above was sufficient to let the thread fully
384 // stop.
385 ASSERT_FALSE(a->IsRunning());
386
387 // Restarting it should not be allowed.
388 EXPECT_DCHECK_DEATH(a->Start());
389 }
390 #endif
391
TEST_F(ThreadTest,ThreadName)392 TEST_F(ThreadTest, ThreadName) {
393 Thread a("ThreadName");
394 EXPECT_TRUE(a.Start());
395 EXPECT_EQ("ThreadName", a.thread_name());
396 }
397
TEST_F(ThreadTest,ThreadId)398 TEST_F(ThreadTest, ThreadId) {
399 Thread a("ThreadId0");
400 Thread b("ThreadId1");
401 a.Start();
402 b.Start();
403
404 // Post a task that calls GetThreadId() on the created thread.
405 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
406 base::WaitableEvent::InitialState::NOT_SIGNALED);
407 base::PlatformThreadId id_from_new_thread;
408 a.task_runner()->PostTask(
409 FROM_HERE,
410 base::BindOnce(ReturnThreadId, &a, &id_from_new_thread, &event));
411
412 // Call GetThreadId() on the current thread before calling event.Wait() so
413 // that this test can find a race issue with TSAN.
414 base::PlatformThreadId id_from_current_thread = a.GetThreadId();
415
416 // Check if GetThreadId() returns consistent value in both threads.
417 event.Wait();
418 EXPECT_EQ(id_from_current_thread, id_from_new_thread);
419
420 // A started thread should have a valid ID.
421 EXPECT_NE(base::kInvalidThreadId, a.GetThreadId());
422 EXPECT_NE(base::kInvalidThreadId, b.GetThreadId());
423
424 // Each thread should have a different thread ID.
425 EXPECT_NE(a.GetThreadId(), b.GetThreadId());
426 }
427
TEST_F(ThreadTest,ThreadIdWithRestart)428 TEST_F(ThreadTest, ThreadIdWithRestart) {
429 Thread a("ThreadIdWithRestart");
430 base::PlatformThreadId previous_id = base::kInvalidThreadId;
431
432 for (size_t i = 0; i < 16; ++i) {
433 EXPECT_TRUE(a.Start());
434 base::PlatformThreadId current_id = a.GetThreadId();
435 EXPECT_NE(previous_id, current_id);
436 previous_id = current_id;
437 a.Stop();
438 }
439 }
440
441 // Make sure Init() is called after Start() and before
442 // WaitUntilThreadInitialized() returns.
TEST_F(ThreadTest,SleepInsideInit)443 TEST_F(ThreadTest, SleepInsideInit) {
444 SleepInsideInitThread t;
445 EXPECT_FALSE(t.InitCalled());
446 t.StartAndWaitForTesting();
447 EXPECT_TRUE(t.InitCalled());
448 }
449
450 // Make sure that the destruction sequence is:
451 //
452 // (1) Thread::CleanUp()
453 // (2) MessageLoop::~MessageLoop()
454 // MessageLoopCurrent::DestructionObservers called.
TEST_F(ThreadTest,CleanUp)455 TEST_F(ThreadTest, CleanUp) {
456 EventList captured_events;
457 CapturingDestructionObserver loop_destruction_observer(&captured_events);
458
459 {
460 // Start a thread which writes its event into |captured_events|.
461 CaptureToEventList t(&captured_events);
462 EXPECT_TRUE(t.Start());
463 EXPECT_TRUE(t.task_runner());
464 EXPECT_TRUE(t.IsRunning());
465
466 // Register an observer that writes into |captured_events| once the
467 // thread's message loop is destroyed.
468 t.task_runner()->PostTask(
469 FROM_HERE,
470 base::BindOnce(&RegisterDestructionObserver,
471 base::Unretained(&loop_destruction_observer)));
472
473 // Upon leaving this scope, the thread is deleted.
474 }
475
476 // Check the order of events during shutdown.
477 ASSERT_EQ(static_cast<size_t>(THREAD_NUM_EVENTS), captured_events.size());
478 EXPECT_EQ(THREAD_EVENT_INIT, captured_events[0]);
479 EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]);
480 EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]);
481 }
482
TEST_F(ThreadTest,ThreadNotStarted)483 TEST_F(ThreadTest, ThreadNotStarted) {
484 Thread a("Inert");
485 EXPECT_FALSE(a.task_runner());
486 }
487
TEST_F(ThreadTest,MultipleWaitUntilThreadStarted)488 TEST_F(ThreadTest, MultipleWaitUntilThreadStarted) {
489 Thread a("MultipleWaitUntilThreadStarted");
490 EXPECT_TRUE(a.Start());
491 // It's OK to call WaitUntilThreadStarted() multiple times.
492 EXPECT_TRUE(a.WaitUntilThreadStarted());
493 EXPECT_TRUE(a.WaitUntilThreadStarted());
494 }
495
TEST_F(ThreadTest,FlushForTesting)496 TEST_F(ThreadTest, FlushForTesting) {
497 Thread a("FlushForTesting");
498
499 // Flushing a non-running thread should be a no-op.
500 a.FlushForTesting();
501
502 ASSERT_TRUE(a.Start());
503
504 // Flushing a thread with no tasks shouldn't block.
505 a.FlushForTesting();
506
507 constexpr base::TimeDelta kSleepPerTestTask =
508 base::TimeDelta::FromMilliseconds(50);
509 constexpr size_t kNumSleepTasks = 5;
510
511 const base::TimeTicks ticks_before_post = base::TimeTicks::Now();
512
513 for (size_t i = 0; i < kNumSleepTasks; ++i) {
514 a.task_runner()->PostTask(
515 FROM_HERE,
516 base::BindOnce(&base::PlatformThread::Sleep, kSleepPerTestTask));
517 }
518
519 // All tasks should have executed, as reflected by the elapsed time.
520 a.FlushForTesting();
521 EXPECT_GE(base::TimeTicks::Now() - ticks_before_post,
522 kNumSleepTasks * kSleepPerTestTask);
523
524 a.Stop();
525
526 // Flushing a stopped thread should be a no-op.
527 a.FlushForTesting();
528 }
529
TEST_F(ThreadTest,GetTaskExecutorForCurrentThread)530 TEST_F(ThreadTest, GetTaskExecutorForCurrentThread) {
531 Thread a("GetTaskExecutorForCurrentThread");
532 ASSERT_TRUE(a.Start());
533
534 base::WaitableEvent event;
535
536 a.task_runner()->PostTask(
537 FROM_HERE, base::BindLambdaForTesting([&]() {
538 EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull());
539 event.Signal();
540 }));
541
542 event.Wait();
543 a.Stop();
544 }
545
546 namespace {
547
548 class SequenceManagerThreadDelegate : public Thread::Delegate {
549 public:
SequenceManagerThreadDelegate()550 SequenceManagerThreadDelegate()
551 : sequence_manager_(
552 base::sequence_manager::CreateUnboundSequenceManager()),
553 task_queue_(
554 sequence_manager_
555 ->CreateTaskQueueWithType<base::sequence_manager::TaskQueue>(
556 base::sequence_manager::TaskQueue::Spec("default_tq"))) {
557 sequence_manager_->SetDefaultTaskRunner(GetDefaultTaskRunner());
558 }
559
~SequenceManagerThreadDelegate()560 ~SequenceManagerThreadDelegate() override {}
561
562 // Thread::Delegate:
563
GetDefaultTaskRunner()564 scoped_refptr<base::SingleThreadTaskRunner> GetDefaultTaskRunner() override {
565 return task_queue_->task_runner();
566 }
567
BindToCurrentThread(base::TimerSlack timer_slack)568 void BindToCurrentThread(base::TimerSlack timer_slack) override {
569 sequence_manager_->BindToMessagePump(
570 base::MessagePump::Create(base::MessagePumpType::DEFAULT));
571 sequence_manager_->SetTimerSlack(timer_slack);
572 }
573
574 private:
575 std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager_;
576 scoped_refptr<base::sequence_manager::TaskQueue> task_queue_;
577
578 DISALLOW_COPY_AND_ASSIGN(SequenceManagerThreadDelegate);
579 };
580
581 } // namespace
582
TEST_F(ThreadTest,ProvidedThreadDelegate)583 TEST_F(ThreadTest, ProvidedThreadDelegate) {
584 Thread thread("ThreadDelegate");
585 base::Thread::Options options;
586 options.delegate = new SequenceManagerThreadDelegate();
587 thread.StartWithOptions(options);
588
589 base::WaitableEvent event;
590
591 options.delegate->GetDefaultTaskRunner()->PostTask(
592 FROM_HERE,
593 base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event)));
594 event.Wait();
595
596 thread.Stop();
597 }
598