1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/synchronization/waitable_event.h"
6
7 #include <windows.h>
8 #include <stddef.h>
9
10 #include <algorithm>
11 #include <utility>
12
13 #include "base/debug/activity_tracker.h"
14 #include "base/logging.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/optional.h"
17 #include "base/threading/scoped_blocking_call.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/time/time.h"
20 #include "base/time/time_override.h"
21
22 namespace base {
23
WaitableEvent(ResetPolicy reset_policy,InitialState initial_state)24 WaitableEvent::WaitableEvent(ResetPolicy reset_policy,
25 InitialState initial_state)
26 : handle_(CreateEvent(nullptr,
27 reset_policy == ResetPolicy::MANUAL,
28 initial_state == InitialState::SIGNALED,
29 nullptr)) {
30 // We're probably going to crash anyways if this is ever NULL, so we might as
31 // well make our stack reports more informative by crashing here.
32 CHECK(handle_.IsValid());
33 }
34
WaitableEvent(win::ScopedHandle handle)35 WaitableEvent::WaitableEvent(win::ScopedHandle handle)
36 : handle_(std::move(handle)) {
37 CHECK(handle_.IsValid()) << "Tried to create WaitableEvent from NULL handle";
38 }
39
40 WaitableEvent::~WaitableEvent() = default;
41
Reset()42 void WaitableEvent::Reset() {
43 ResetEvent(handle_.Get());
44 }
45
Signal()46 void WaitableEvent::Signal() {
47 SetEvent(handle_.Get());
48 }
49
IsSignaled()50 bool WaitableEvent::IsSignaled() {
51 DWORD result = WaitForSingleObject(handle_.Get(), 0);
52 DCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
53 << "Unexpected WaitForSingleObject result " << result;
54 return result == WAIT_OBJECT_0;
55 }
56
Wait()57 void WaitableEvent::Wait() {
58 // Record the event that this thread is blocking upon (for hang diagnosis) and
59 // consider it blocked for scheduling purposes. Ignore this for non-blocking
60 // WaitableEvents.
61 Optional<debug::ScopedEventWaitActivity> event_activity;
62 Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
63 scoped_blocking_call;
64 if (waiting_is_blocking_) {
65 event_activity.emplace(this);
66 scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
67 }
68
69 DWORD result = WaitForSingleObject(handle_.Get(), INFINITE);
70 // It is most unexpected that this should ever fail. Help consumers learn
71 // about it if it should ever fail.
72 DPCHECK(result != WAIT_FAILED);
73 DCHECK_EQ(WAIT_OBJECT_0, result);
74 }
75
TimedWait(const TimeDelta & wait_delta)76 bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
77 if (wait_delta <= TimeDelta())
78 return IsSignaled();
79
80 // Record the event that this thread is blocking upon (for hang diagnosis) and
81 // consider it blocked for scheduling purposes. Ignore this for non-blocking
82 // WaitableEvents.
83 Optional<debug::ScopedEventWaitActivity> event_activity;
84 Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
85 scoped_blocking_call;
86 if (waiting_is_blocking_) {
87 event_activity.emplace(this);
88 scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
89 }
90
91 // TimeTicks takes care of overflow but we special case is_max() nonetheless
92 // to avoid invoking TimeTicksNowIgnoringOverride() unnecessarily.
93 // WaitForSingleObject(handle_.Get(), INFINITE) doesn't spuriously wakeup so
94 // we don't need to worry about is_max() for the increment phase of the loop.
95 const TimeTicks end_time =
96 wait_delta.is_max() ? TimeTicks::Max()
97 : subtle::TimeTicksNowIgnoringOverride() + wait_delta;
98 for (TimeDelta remaining = wait_delta; remaining > TimeDelta();
99 remaining = end_time - subtle::TimeTicksNowIgnoringOverride()) {
100 // Truncate the timeout to milliseconds, rounded up to avoid spinning
101 // (either by returning too early or because a < 1ms timeout on Windows
102 // tends to return immediately).
103 const DWORD timeout_ms =
104 remaining.is_max()
105 ? INFINITE
106 : saturated_cast<DWORD>(remaining.InMillisecondsRoundedUp());
107 const DWORD result = WaitForSingleObject(handle_.Get(), timeout_ms);
108 DCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
109 << "Unexpected WaitForSingleObject result " << result;
110 switch (result) {
111 case WAIT_OBJECT_0:
112 return true;
113 case WAIT_TIMEOUT:
114 // TimedWait can time out earlier than the specified |timeout| on
115 // Windows. To make this consistent with the posix implementation we
116 // should guarantee that TimedWait doesn't return earlier than the
117 // specified |max_time| and wait again for the remaining time.
118 continue;
119 }
120 }
121 return false;
122 }
123
124 // static
WaitMany(WaitableEvent ** events,size_t count)125 size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) {
126 DCHECK(count) << "Cannot wait on no events";
127 internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
128 FROM_HERE, BlockingType::MAY_BLOCK);
129 // Record an event (the first) that this thread is blocking upon.
130 debug::ScopedEventWaitActivity event_activity(events[0]);
131
132 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
133 CHECK_LE(count, static_cast<size_t>(MAXIMUM_WAIT_OBJECTS))
134 << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany";
135
136 for (size_t i = 0; i < count; ++i)
137 handles[i] = events[i]->handle();
138
139 // The cast is safe because count is small - see the CHECK above.
140 DWORD result =
141 WaitForMultipleObjects(static_cast<DWORD>(count),
142 handles,
143 FALSE, // don't wait for all the objects
144 INFINITE); // no timeout
145 if (result >= WAIT_OBJECT_0 + count) {
146 DPLOG(FATAL) << "WaitForMultipleObjects failed";
147 return 0;
148 }
149
150 return result - WAIT_OBJECT_0;
151 }
152
153 } // namespace base
154