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