1 // Copyright 2022 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/synchronization/waitable_event.h"
11
12 #include "base/check.h"
13 #include "base/threading/scoped_blocking_call.h"
14 #include "base/trace_event/base_tracing.h"
15 #include "base/tracing_buildflags.h"
16
17 namespace base {
18
~WaitableEvent()19 WaitableEvent::~WaitableEvent() {
20 #if BUILDFLAG(ENABLE_BASE_TRACING)
21 // As requested in the documentation of perfetto::Flow::FromPointer, we should
22 // emit a TerminatingFlow(this) from our destructor if we ever emitted a
23 // Flow(this) which may be unmatched since the ptr value of `this` may be
24 // reused after this destructor. This can happen if a signaled event is never
25 // waited upon (or isn't the one to satisfy a WaitMany condition).
26 if (!only_used_while_idle_) {
27 // Check the tracing state to avoid an unnecessary syscall on destruction
28 // (which can be performance sensitive, crbug.com/40275035).
29 static const uint8_t* flow_enabled =
30 TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("wakeup.flow,toplevel.flow");
31 if (*flow_enabled && IsSignaled()) {
32 TRACE_EVENT_INSTANT("wakeup.flow,toplevel.flow",
33 "~WaitableEvent while Signaled",
34 perfetto::TerminatingFlow::FromPointer(this));
35 }
36 }
37 #endif // BUILDFLAG(ENABLE_BASE_TRACING)
38 }
39
Signal()40 void WaitableEvent::Signal() {
41 // Must be ordered before SignalImpl() to guarantee it's emitted before the
42 // matching TerminatingFlow in TimedWait().
43 if (!only_used_while_idle_) {
44 TRACE_EVENT_INSTANT("wakeup.flow,toplevel.flow", "WaitableEvent::Signal",
45 perfetto::Flow::FromPointer(this));
46 }
47 SignalImpl();
48 }
49
Wait()50 void WaitableEvent::Wait() {
51 const bool result = TimedWait(TimeDelta::Max());
52 DCHECK(result) << "TimedWait() should never fail with infinite timeout";
53 }
54
TimedWait(TimeDelta wait_delta)55 bool WaitableEvent::TimedWait(TimeDelta wait_delta) {
56 if (wait_delta <= TimeDelta())
57 return IsSignaled();
58
59 // Consider this thread blocked for scheduling purposes. Ignore this for
60 // non-blocking WaitableEvents.
61 std::optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
62 scoped_blocking_call;
63 if (!only_used_while_idle_) {
64 scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
65 }
66
67 const bool result = TimedWaitImpl(wait_delta);
68
69 if (result && !only_used_while_idle_) {
70 TRACE_EVENT_INSTANT("wakeup.flow,toplevel.flow",
71 "WaitableEvent::Wait Complete",
72 perfetto::TerminatingFlow::FromPointer(this));
73 }
74
75 return result;
76 }
77
WaitMany(WaitableEvent ** events,size_t count)78 size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) {
79 DCHECK(count) << "Cannot wait on no events";
80 internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
81 FROM_HERE, BlockingType::MAY_BLOCK);
82
83 const size_t signaled_id = WaitManyImpl(events, count);
84 WaitableEvent* const signaled_event = events[signaled_id];
85 if (!signaled_event->only_used_while_idle_) {
86 TRACE_EVENT_INSTANT("wakeup.flow,toplevel.flow",
87 "WaitableEvent::WaitMany Complete",
88 perfetto::TerminatingFlow::FromPointer(signaled_event));
89 }
90 return signaled_id;
91 }
92
93 } // namespace base
94