| // 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 <windows.h> |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #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" |
| #include "starboard/types.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<DWORD>(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<size_t>(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<DWORD>(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 |