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