// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/synchronization/waitable_event.h" #include #include #include #include #include "base/debug/activity_tracker.h" #include "base/logging.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" namespace base { 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_.IsValid()); } WaitableEvent::WaitableEvent(win::ScopedHandle handle) : handle_(std::move(handle)) { CHECK(handle_.IsValid()) << "Tried to create WaitableEvent from NULL handle"; } WaitableEvent::~WaitableEvent() = default; void WaitableEvent::Reset() { ResetEvent(handle_.Get()); } void WaitableEvent::Signal() { SetEvent(handle_.Get()); } bool WaitableEvent::IsSignaled() { DWORD result = WaitForSingleObject(handle_.Get(), 0); DCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) << "Unexpected WaitForSingleObject result " << result; return result == WAIT_OBJECT_0; } void WaitableEvent::Wait() { internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( BlockingType::MAY_BLOCK); // Record the event that this thread is blocking upon (for hang diagnosis). base::debug::ScopedEventWaitActivity event_activity(this); DWORD result = WaitForSingleObject(handle_.Get(), INFINITE); // It is most unexpected that this should ever fail. Help consumers learn // about it if it should ever fail. DPCHECK(result != WAIT_FAILED); DCHECK_EQ(WAIT_OBJECT_0, result); } namespace { // Helper function called from TimedWait and TimedWaitUntil. bool WaitUntil(HANDLE handle, const TimeTicks& now, const TimeTicks& end_time) { internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( BlockingType::MAY_BLOCK); TimeDelta delta = end_time - now; DCHECK_GT(delta, TimeDelta()); do { // On Windows, waiting for less than 1 ms results in WaitForSingleObject // returning promptly which may result in the caller code spinning. // We need to ensure that we specify at least the minimally possible 1 ms // delay unless the initial timeout was exactly zero. delta = std::max(delta, TimeDelta::FromMilliseconds(1)); // Truncate the timeout to milliseconds. DWORD timeout_ms = saturated_cast(delta.InMilliseconds()); DWORD result = WaitForSingleObject(handle, timeout_ms); DCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) << "Unexpected WaitForSingleObject result " << result; switch (result) { case WAIT_OBJECT_0: return true; case 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. delta = end_time - TimeTicks::Now(); break; } } while (delta > TimeDelta()); return false; } } // namespace bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) { DCHECK_GE(wait_delta, TimeDelta()); if (wait_delta.is_zero()) return IsSignaled(); internal::AssertBaseSyncPrimitivesAllowed(); // Record the event that this thread is blocking upon (for hang diagnosis). base::debug::ScopedEventWaitActivity event_activity(this); TimeTicks now(TimeTicks::Now()); // TimeTicks takes care of overflow including the cases when wait_delta // is a maximum value. return WaitUntil(handle_.Get(), now, now + wait_delta); } bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { if (end_time.is_null()) return IsSignaled(); internal::AssertBaseSyncPrimitivesAllowed(); // Record the event that this thread is blocking upon (for hang diagnosis). base::debug::ScopedEventWaitActivity event_activity(this); TimeTicks now(TimeTicks::Now()); if (end_time <= now) return IsSignaled(); return WaitUntil(handle_.Get(), now, end_time); } // static size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) { DCHECK(count) << "Cannot wait on no events"; internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( BlockingType::MAY_BLOCK); // Record an event (the first) that this thread is blocking upon. base::debug::ScopedEventWaitActivity event_activity(events[0]); 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