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