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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
// 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 <stddef.h>
#include <algorithm>
#include <utility>
#include "base/debug/activity_tracker.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.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() {
base::ThreadRestrictions::AssertWaitAllowed();
// 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.
DCHECK_EQ(WAIT_OBJECT_0, result) << "WaitForSingleObject failed";
}
namespace {
// Helper function called from TimedWait and TimedWaitUntil.
bool WaitUntil(HANDLE handle, const TimeTicks& now, const TimeTicks& end_time) {
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();
base::ThreadRestrictions::AssertWaitAllowed();
// 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();
base::ThreadRestrictions::AssertWaitAllowed();
// 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";
base::ThreadRestrictions::AssertWaitAllowed();
// 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
|