// Copyright 2011 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 #include #include #include #include #include "base/compiler_specific.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" #include "base/logging.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/threading/scoped_blocking_call.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "base/time/time_override.h" namespace base { namespace { [[nodiscard]] debug::ScopedCrashKeyString SetLastErrorCrashKey( DWORD last_error) { static auto* const key = debug::AllocateCrashKeyString( "WaitableEvent-last_error", debug::CrashKeySize::Size32); return debug::ScopedCrashKeyString(key, NumberToString(last_error)); } NOINLINE void ReportInvalidWaitableEventResult(DWORD result, DWORD last_error) { SCOPED_CRASH_KEY_NUMBER("WaitableEvent", "result", result); debug::ScopedCrashKeyString last_error_key = SetLastErrorCrashKey(last_error); base::debug::DumpWithoutCrashing(); // https://crbug.com/1478972. } } // namespace WaitableEvent::WaitableEvent(ResetPolicy reset_policy, InitialState initial_state) : handle_(CreateEvent(nullptr, reset_policy == ResetPolicy::MANUAL, initial_state == InitialState::SIGNALED, nullptr)) { // We're probably going to crash anyways if this is ever NULL, so we might as // well make our stack reports more informative by crashing here. CHECK(handle_.is_valid()); } WaitableEvent::WaitableEvent(win::ScopedHandle handle) : handle_(std::move(handle)) { CHECK(handle_.is_valid()) << "Tried to create WaitableEvent from NULL handle"; } void WaitableEvent::Reset() { ResetEvent(handle_.get()); } void WaitableEvent::SignalImpl() { SetEvent(handle_.get()); } bool WaitableEvent::IsSignaled() const { DWORD result = WaitForSingleObject(handle_.get(), 0); if (result != WAIT_OBJECT_0 && result != WAIT_TIMEOUT) { ReportInvalidWaitableEventResult(result, ::GetLastError()); } return result == WAIT_OBJECT_0; } bool WaitableEvent::TimedWaitImpl(TimeDelta wait_delta) { // TimeTicks takes care of overflow but we special case is_max() nonetheless // to avoid invoking TimeTicksNowIgnoringOverride() unnecessarily. // WaitForSingleObject(handle_.Get(), INFINITE) doesn't spuriously wakeup so // we don't need to worry about is_max() for the increment phase of the loop. const TimeTicks end_time = wait_delta.is_max() ? TimeTicks::Max() : subtle::TimeTicksNowIgnoringOverride() + wait_delta; for (TimeDelta remaining = wait_delta; remaining.is_positive(); remaining = end_time - subtle::TimeTicksNowIgnoringOverride()) { // Truncate the timeout to milliseconds, rounded up to avoid spinning // (either by returning too early or because a < 1ms timeout on Windows // tends to return immediately). const DWORD timeout_ms = remaining.is_max() ? INFINITE : saturated_cast(remaining.InMillisecondsRoundedUp()); const DWORD result = WaitForSingleObject(handle_.get(), timeout_ms); if (result == WAIT_OBJECT_0) { // The object is signaled. return true; } if (result == WAIT_TIMEOUT) { // TimedWait can time out earlier than the specified |timeout| on // Windows. To make this consistent with the posix implementation we // should guarantee that TimedWait doesn't return earlier than the // specified |max_time| and wait again for the remaining time. continue; } // Failures are likely due to ERROR_INVALID_HANDLE. This unrecoverable // error likely means that the waited-on object has been closed elsewhere, // possibly due to a double-close on an unrelated HANDLE. Crash // immediately since it is not possible to reason about the state of the // process in this case. if (result == WAIT_FAILED) { debug::ScopedCrashKeyString last_error_key = SetLastErrorCrashKey(::GetLastError()); NOTREACHED(); } if (wait_delta.is_max()) { // The only other documented result value is `WAIT_ABANDONED`. This nor // any other result should ever be emitted. ReportInvalidWaitableEventResult(result, ::GetLastError()); } } return false; } // static size_t WaitableEvent::WaitManyImpl(WaitableEvent** events, size_t count) { HANDLE handles[MAXIMUM_WAIT_OBJECTS]; CHECK_LE(count, static_cast(MAXIMUM_WAIT_OBJECTS)) << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany"; for (size_t i = 0; i < count; ++i) handles[i] = events[i]->handle(); // The cast is safe because count is small - see the CHECK above. DWORD result = WaitForMultipleObjects(static_cast(count), handles, FALSE, // don't wait for all the objects INFINITE); // no timeout if (result >= WAIT_OBJECT_0 + count) { DPLOG(FATAL) << "WaitForMultipleObjects failed"; return 0; } return result - WAIT_OBJECT_0; } } // namespace base