forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwaitable_event_win.cc
159 lines (128 loc) · 5.36 KB
/
waitable_event_win.cc
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