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 "base/allocator/partition_allocator/src/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
44 WorkerThreadWaitableEvent::~WorkerThreadWaitableEvent() = default;
45
JoinForTesting()46 void WorkerThreadWaitableEvent::JoinForTesting() {
47 DCHECK(!join_called_for_testing_.IsSet());
48 join_called_for_testing_.Set();
49 delegate_->wake_up_event_.Signal();
50
51 PlatformThreadHandle thread_handle;
52
53 {
54 CheckedAutoLock auto_lock(thread_lock_);
55
56 if (thread_handle_.is_null()) {
57 return;
58 }
59
60 thread_handle = thread_handle_;
61 // Reset |thread_handle_| so it isn't joined by the destructor.
62 thread_handle_ = PlatformThreadHandle();
63 }
64
65 PlatformThread::Join(thread_handle);
66 }
67
Cleanup()68 void WorkerThreadWaitableEvent::Cleanup() {
69 DCHECK(!should_exit_.IsSet());
70 should_exit_.Set();
71 delegate_->wake_up_event_.Signal();
72 }
73
WakeUp()74 void WorkerThreadWaitableEvent::WakeUp() {
75 // Signalling an event can deschedule the current thread. Since being
76 // descheduled while holding a lock is undesirable (https://crbug.com/890978),
77 // assert that no lock is held by the current thread.
78 CheckedLock::AssertNoLockHeldOnCurrentThread();
79 // Calling WakeUp() after Cleanup() or Join() is wrong because the
80 // WorkerThread cannot run more tasks.
81 DCHECK(!join_called_for_testing_.IsSet());
82 DCHECK(!should_exit_.IsSet());
83 TRACE_EVENT_INSTANT("wakeup.flow", "WorkerThreadWaitableEvent::WakeUp",
84 perfetto::Flow::FromPointer(this));
85
86 delegate_->wake_up_event_.Signal();
87 }
88
delegate()89 WorkerThreadWaitableEvent::Delegate* WorkerThreadWaitableEvent::delegate() {
90 return delegate_.get();
91 }
92
93 } // namespace base::internal
94