xref: /aosp_15_r20/external/cronet/base/synchronization/waitable_event.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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