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