1 // Copyright 2023 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/task/thread_pool/worker_thread_waitable_event.h"
6
7 #include "base/debug/alias.h"
8 #include "base/synchronization/waitable_event.h"
9 #include "base/task/thread_pool/task_tracker.h"
10 #include "base/task/thread_pool/worker_thread_observer.h"
11 #include "base/time/time.h"
12 #include "base/trace_event/base_tracing.h"
13
14 #if BUILDFLAG(IS_APPLE)
15 #include "base/apple/scoped_nsautorelease_pool.h"
16 #endif
17
18 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
19 PA_CONFIG(THREAD_CACHE_SUPPORTED)
20 #include "partition_alloc/thread_cache.h"
21 #endif
22
23 namespace base::internal {
24
TimedWait(TimeDelta timeout)25 bool WorkerThreadWaitableEvent::Delegate::TimedWait(TimeDelta timeout) {
26 return wake_up_event_.TimedWait(timeout);
27 }
28
WorkerThreadWaitableEvent(ThreadType thread_type_hint,std::unique_ptr<Delegate> delegate,TrackedRef<TaskTracker> task_tracker,size_t sequence_num,const CheckedLock * predecessor_lock)29 WorkerThreadWaitableEvent::WorkerThreadWaitableEvent(
30 ThreadType thread_type_hint,
31 std::unique_ptr<Delegate> delegate,
32 TrackedRef<TaskTracker> task_tracker,
33 size_t sequence_num,
34 const CheckedLock* predecessor_lock)
35 : WorkerThread(thread_type_hint,
36 task_tracker,
37 sequence_num,
38 predecessor_lock),
39 delegate_(std::move(delegate)) {
40 DCHECK(delegate_);
41 delegate_->wake_up_event_.declare_only_used_while_idle();
42 }
43
~WorkerThreadWaitableEvent()44 WorkerThreadWaitableEvent::~WorkerThreadWaitableEvent() {
45 Destroy();
46 }
47
JoinForTesting()48 void WorkerThreadWaitableEvent::JoinForTesting() {
49 DCHECK(!join_called_for_testing_.IsSet());
50 join_called_for_testing_.Set();
51 delegate_->wake_up_event_.Signal();
52
53 PlatformThreadHandle thread_handle;
54
55 {
56 CheckedAutoLock auto_lock(thread_lock_);
57
58 if (thread_handle_.is_null()) {
59 return;
60 }
61
62 thread_handle = thread_handle_;
63 // Reset |thread_handle_| so it isn't joined by the destructor.
64 thread_handle_ = PlatformThreadHandle();
65 }
66
67 PlatformThread::Join(thread_handle);
68 }
69
Cleanup()70 void WorkerThreadWaitableEvent::Cleanup() {
71 DCHECK(!should_exit_.IsSet());
72 should_exit_.Set();
73 delegate_->wake_up_event_.Signal();
74 }
75
join_called_for_testing() const76 bool WorkerThreadWaitableEvent::join_called_for_testing() const {
77 return join_called_for_testing_.IsSet();
78 }
79
WakeUp()80 void WorkerThreadWaitableEvent::WakeUp() {
81 // Signalling an event can deschedule the current thread. Since being
82 // descheduled while holding a lock is undesirable (https://crbug.com/890978),
83 // assert that no lock is held by the current thread.
84 CheckedLock::AssertNoLockHeldOnCurrentThread();
85 // Calling WakeUp() after Cleanup() or Join() is wrong because the
86 // WorkerThread cannot run more tasks.
87 DCHECK(!join_called_for_testing_.IsSet());
88 DCHECK(!should_exit_.IsSet());
89 TRACE_EVENT_INSTANT("wakeup.flow", "WorkerThreadWaitableEvent::WakeUp",
90 perfetto::Flow::FromPointer(this));
91
92 delegate_->wake_up_event_.Signal();
93 }
94
delegate()95 WorkerThreadWaitableEvent::Delegate* WorkerThreadWaitableEvent::delegate() {
96 return delegate_.get();
97 }
98
99 } // namespace base::internal
100