xref: /aosp_15_r20/external/cronet/base/synchronization/waitable_event.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #ifndef BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_
6*6777b538SAndroid Build Coastguard Worker #define BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_
7*6777b538SAndroid Build Coastguard Worker 
8*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
9*6777b538SAndroid Build Coastguard Worker 
10*6777b538SAndroid Build Coastguard Worker #include "base/base_export.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/compiler_specific.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ptr.h"
13*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
14*6777b538SAndroid Build Coastguard Worker 
15*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
16*6777b538SAndroid Build Coastguard Worker #include "base/win/scoped_handle.h"
17*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_APPLE)
18*6777b538SAndroid Build Coastguard Worker #include <mach/mach.h>
19*6777b538SAndroid Build Coastguard Worker 
20*6777b538SAndroid Build Coastguard Worker #include <list>
21*6777b538SAndroid Build Coastguard Worker #include <memory>
22*6777b538SAndroid Build Coastguard Worker 
23*6777b538SAndroid Build Coastguard Worker #include "base/apple/scoped_mach_port.h"
24*6777b538SAndroid Build Coastguard Worker #include "base/functional/callback_forward.h"
25*6777b538SAndroid Build Coastguard Worker #include "base/memory/ref_counted.h"
26*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
27*6777b538SAndroid Build Coastguard Worker #include <list>
28*6777b538SAndroid Build Coastguard Worker #include <utility>
29*6777b538SAndroid Build Coastguard Worker 
30*6777b538SAndroid Build Coastguard Worker #include "base/memory/ref_counted.h"
31*6777b538SAndroid Build Coastguard Worker #include "base/synchronization/lock.h"
32*6777b538SAndroid Build Coastguard Worker #endif
33*6777b538SAndroid Build Coastguard Worker 
34*6777b538SAndroid Build Coastguard Worker namespace base {
35*6777b538SAndroid Build Coastguard Worker 
36*6777b538SAndroid Build Coastguard Worker class TimeDelta;
37*6777b538SAndroid Build Coastguard Worker 
38*6777b538SAndroid Build Coastguard Worker // A WaitableEvent can be a useful thread synchronization tool when you want to
39*6777b538SAndroid Build Coastguard Worker // allow one thread to wait for another thread to finish some work. For
40*6777b538SAndroid Build Coastguard Worker // non-Windows systems, this can only be used from within a single address
41*6777b538SAndroid Build Coastguard Worker // space.
42*6777b538SAndroid Build Coastguard Worker //
43*6777b538SAndroid Build Coastguard Worker // Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to
44*6777b538SAndroid Build Coastguard Worker // protect a simple boolean value.  However, if you find yourself using a
45*6777b538SAndroid Build Coastguard Worker // WaitableEvent in conjunction with a Lock to wait for a more complex state
46*6777b538SAndroid Build Coastguard Worker // change (e.g., for an item to be added to a queue), then you should probably
47*6777b538SAndroid Build Coastguard Worker // be using a ConditionVariable instead of a WaitableEvent.
48*6777b538SAndroid Build Coastguard Worker //
49*6777b538SAndroid Build Coastguard Worker // NOTE: On Windows, this class provides a subset of the functionality afforded
50*6777b538SAndroid Build Coastguard Worker // by a Windows event object.  This is intentional.  If you are writing Windows
51*6777b538SAndroid Build Coastguard Worker // specific code and you need other features of a Windows event, then you might
52*6777b538SAndroid Build Coastguard Worker // be better off just using an Windows event directly.
53*6777b538SAndroid Build Coastguard Worker class BASE_EXPORT WaitableEvent {
54*6777b538SAndroid Build Coastguard Worker  public:
55*6777b538SAndroid Build Coastguard Worker   // Indicates whether a WaitableEvent should automatically reset the event
56*6777b538SAndroid Build Coastguard Worker   // state after a single waiting thread has been released or remain signaled
57*6777b538SAndroid Build Coastguard Worker   // until Reset() is manually invoked.
58*6777b538SAndroid Build Coastguard Worker   enum class ResetPolicy { MANUAL, AUTOMATIC };
59*6777b538SAndroid Build Coastguard Worker 
60*6777b538SAndroid Build Coastguard Worker   // Indicates whether a new WaitableEvent should start in a signaled state or
61*6777b538SAndroid Build Coastguard Worker   // not.
62*6777b538SAndroid Build Coastguard Worker   enum class InitialState { SIGNALED, NOT_SIGNALED };
63*6777b538SAndroid Build Coastguard Worker 
64*6777b538SAndroid Build Coastguard Worker   // Constructs a WaitableEvent with policy and initial state as detailed in
65*6777b538SAndroid Build Coastguard Worker   // the above enums.
66*6777b538SAndroid Build Coastguard Worker   WaitableEvent(ResetPolicy reset_policy = ResetPolicy::MANUAL,
67*6777b538SAndroid Build Coastguard Worker                 InitialState initial_state = InitialState::NOT_SIGNALED);
68*6777b538SAndroid Build Coastguard Worker 
69*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
70*6777b538SAndroid Build Coastguard Worker   // Create a WaitableEvent from an Event HANDLE which has already been
71*6777b538SAndroid Build Coastguard Worker   // created. This objects takes ownership of the HANDLE and will close it when
72*6777b538SAndroid Build Coastguard Worker   // deleted.
73*6777b538SAndroid Build Coastguard Worker   explicit WaitableEvent(win::ScopedHandle event_handle);
74*6777b538SAndroid Build Coastguard Worker #endif
75*6777b538SAndroid Build Coastguard Worker 
76*6777b538SAndroid Build Coastguard Worker   WaitableEvent(const WaitableEvent&) = delete;
77*6777b538SAndroid Build Coastguard Worker   WaitableEvent& operator=(const WaitableEvent&) = delete;
78*6777b538SAndroid Build Coastguard Worker 
79*6777b538SAndroid Build Coastguard Worker   ~WaitableEvent();
80*6777b538SAndroid Build Coastguard Worker 
81*6777b538SAndroid Build Coastguard Worker   // Put the event in the un-signaled state.
82*6777b538SAndroid Build Coastguard Worker   void Reset();
83*6777b538SAndroid Build Coastguard Worker 
84*6777b538SAndroid Build Coastguard Worker   // Put the event in the signaled state.  Causing any thread blocked on Wait
85*6777b538SAndroid Build Coastguard Worker   // to be woken up.
86*6777b538SAndroid Build Coastguard Worker   void Signal();
87*6777b538SAndroid Build Coastguard Worker 
88*6777b538SAndroid Build Coastguard Worker   // Returns true if the event is in the signaled state, else false.  If this
89*6777b538SAndroid Build Coastguard Worker   // is not a manual reset event, then this test will cause a reset.
90*6777b538SAndroid Build Coastguard Worker   bool IsSignaled();
91*6777b538SAndroid Build Coastguard Worker 
92*6777b538SAndroid Build Coastguard Worker   // Wait indefinitely for the event to be signaled. Wait's return "happens
93*6777b538SAndroid Build Coastguard Worker   // after" |Signal| has completed. This means that it's safe for a
94*6777b538SAndroid Build Coastguard Worker   // WaitableEvent to synchronise its own destruction, like this:
95*6777b538SAndroid Build Coastguard Worker   //
96*6777b538SAndroid Build Coastguard Worker   //   WaitableEvent *e = new WaitableEvent;
97*6777b538SAndroid Build Coastguard Worker   //   SendToOtherThread(e);
98*6777b538SAndroid Build Coastguard Worker   //   e->Wait();
99*6777b538SAndroid Build Coastguard Worker   //   delete e;
100*6777b538SAndroid Build Coastguard Worker   NOT_TAIL_CALLED void Wait();
101*6777b538SAndroid Build Coastguard Worker 
102*6777b538SAndroid Build Coastguard Worker   // Wait up until wait_delta has passed for the event to be signaled
103*6777b538SAndroid Build Coastguard Worker   // (real-time; ignores time overrides).  Returns true if the event was
104*6777b538SAndroid Build Coastguard Worker   // signaled. Handles spurious wakeups and guarantees that |wait_delta| will
105*6777b538SAndroid Build Coastguard Worker   // have elapsed if this returns false.
106*6777b538SAndroid Build Coastguard Worker   //
107*6777b538SAndroid Build Coastguard Worker   // TimedWait can synchronise its own destruction like |Wait|.
108*6777b538SAndroid Build Coastguard Worker   NOT_TAIL_CALLED bool TimedWait(TimeDelta wait_delta);
109*6777b538SAndroid Build Coastguard Worker 
110*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
handle()111*6777b538SAndroid Build Coastguard Worker   HANDLE handle() const { return handle_.get(); }
112*6777b538SAndroid Build Coastguard Worker #endif
113*6777b538SAndroid Build Coastguard Worker 
114*6777b538SAndroid Build Coastguard Worker   // Declares that this WaitableEvent will only ever be used by a thread that is
115*6777b538SAndroid Build Coastguard Worker   // idle at the bottom of its stack and waiting for work (in particular, it is
116*6777b538SAndroid Build Coastguard Worker   // not synchronously waiting on this event before resuming ongoing work). This
117*6777b538SAndroid Build Coastguard Worker   // is useful to avoid telling base-internals that this thread is "blocked"
118*6777b538SAndroid Build Coastguard Worker   // when it's merely idle and ready to do work. As such, this is only expected
119*6777b538SAndroid Build Coastguard Worker   // to be used by thread and thread pool impls. In such cases wakeup.flow
120*6777b538SAndroid Build Coastguard Worker   // events aren't emitted on |Signal|/|Wait|, because threading implementations
121*6777b538SAndroid Build Coastguard Worker   // are responsible for emitting the cause of their wakeup from idle.
declare_only_used_while_idle()122*6777b538SAndroid Build Coastguard Worker   void declare_only_used_while_idle() { only_used_while_idle_ = true; }
123*6777b538SAndroid Build Coastguard Worker 
124*6777b538SAndroid Build Coastguard Worker   // Wait, synchronously, on multiple events.
125*6777b538SAndroid Build Coastguard Worker   //   waitables: an array of WaitableEvent pointers
126*6777b538SAndroid Build Coastguard Worker   //   count: the number of elements in @waitables
127*6777b538SAndroid Build Coastguard Worker   //
128*6777b538SAndroid Build Coastguard Worker   // returns: the index of a WaitableEvent which has been signaled.
129*6777b538SAndroid Build Coastguard Worker   //
130*6777b538SAndroid Build Coastguard Worker   // You MUST NOT delete any of the WaitableEvent objects while this wait is
131*6777b538SAndroid Build Coastguard Worker   // happening, however WaitMany's return "happens after" the |Signal| call
132*6777b538SAndroid Build Coastguard Worker   // that caused it has completed, like |Wait|.
133*6777b538SAndroid Build Coastguard Worker   //
134*6777b538SAndroid Build Coastguard Worker   // If more than one WaitableEvent is signaled to unblock WaitMany, the lowest
135*6777b538SAndroid Build Coastguard Worker   // index among them is returned.
136*6777b538SAndroid Build Coastguard Worker   NOT_TAIL_CALLED static size_t WaitMany(WaitableEvent** waitables,
137*6777b538SAndroid Build Coastguard Worker                                          size_t count);
138*6777b538SAndroid Build Coastguard Worker 
139*6777b538SAndroid Build Coastguard Worker   // For asynchronous waiting, see WaitableEventWatcher
140*6777b538SAndroid Build Coastguard Worker 
141*6777b538SAndroid Build Coastguard Worker   // This is a private helper class. It's here because it's used by friends of
142*6777b538SAndroid Build Coastguard Worker   // this class (such as WaitableEventWatcher) to be able to enqueue elements
143*6777b538SAndroid Build Coastguard Worker   // of the wait-list
144*6777b538SAndroid Build Coastguard Worker   class Waiter {
145*6777b538SAndroid Build Coastguard Worker    public:
146*6777b538SAndroid Build Coastguard Worker     // Signal the waiter to wake up.
147*6777b538SAndroid Build Coastguard Worker     //
148*6777b538SAndroid Build Coastguard Worker     // Consider the case of a Waiter which is in multiple WaitableEvent's
149*6777b538SAndroid Build Coastguard Worker     // wait-lists. Each WaitableEvent is automatic-reset and two of them are
150*6777b538SAndroid Build Coastguard Worker     // signaled at the same time. Now, each will wake only the first waiter in
151*6777b538SAndroid Build Coastguard Worker     // the wake-list before resetting. However, if those two waiters happen to
152*6777b538SAndroid Build Coastguard Worker     // be the same object (as can happen if another thread didn't have a chance
153*6777b538SAndroid Build Coastguard Worker     // to dequeue the waiter from the other wait-list in time), two auto-resets
154*6777b538SAndroid Build Coastguard Worker     // will have happened, but only one waiter has been signaled!
155*6777b538SAndroid Build Coastguard Worker     //
156*6777b538SAndroid Build Coastguard Worker     // Because of this, a Waiter may "reject" a wake by returning false. In
157*6777b538SAndroid Build Coastguard Worker     // this case, the auto-reset WaitableEvent shouldn't act as if anything has
158*6777b538SAndroid Build Coastguard Worker     // been notified.
159*6777b538SAndroid Build Coastguard Worker     virtual bool Fire(WaitableEvent* signaling_event) = 0;
160*6777b538SAndroid Build Coastguard Worker 
161*6777b538SAndroid Build Coastguard Worker     // Waiters may implement this in order to provide an extra condition for
162*6777b538SAndroid Build Coastguard Worker     // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the
163*6777b538SAndroid Build Coastguard Worker     // pointers match then this function is called as a final check. See the
164*6777b538SAndroid Build Coastguard Worker     // comments in ~Handle for why.
165*6777b538SAndroid Build Coastguard Worker     virtual bool Compare(void* tag) = 0;
166*6777b538SAndroid Build Coastguard Worker 
167*6777b538SAndroid Build Coastguard Worker    protected:
168*6777b538SAndroid Build Coastguard Worker     virtual ~Waiter() = default;
169*6777b538SAndroid Build Coastguard Worker   };
170*6777b538SAndroid Build Coastguard Worker 
171*6777b538SAndroid Build Coastguard Worker  private:
172*6777b538SAndroid Build Coastguard Worker   friend class WaitableEventWatcher;
173*6777b538SAndroid Build Coastguard Worker 
174*6777b538SAndroid Build Coastguard Worker   // The platform specific portions of Signal, TimedWait, and WaitMany (which do
175*6777b538SAndroid Build Coastguard Worker   // the actual signaling and waiting).
176*6777b538SAndroid Build Coastguard Worker   void SignalImpl();
177*6777b538SAndroid Build Coastguard Worker   bool TimedWaitImpl(TimeDelta wait_delta);
178*6777b538SAndroid Build Coastguard Worker   static size_t WaitManyImpl(WaitableEvent** waitables, size_t count);
179*6777b538SAndroid Build Coastguard Worker 
180*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
181*6777b538SAndroid Build Coastguard Worker   win::ScopedHandle handle_;
182*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_APPLE)
183*6777b538SAndroid Build Coastguard Worker   // Peeks the message queue named by |port| and returns true if a message
184*6777b538SAndroid Build Coastguard Worker   // is present and false if not. If |dequeue| is true, the messsage will be
185*6777b538SAndroid Build Coastguard Worker   // drained from the queue. If |dequeue| is false, the queue will only be
186*6777b538SAndroid Build Coastguard Worker   // peeked. |port| must be a receive right.
187*6777b538SAndroid Build Coastguard Worker   static bool PeekPort(mach_port_t port, bool dequeue);
188*6777b538SAndroid Build Coastguard Worker 
189*6777b538SAndroid Build Coastguard Worker   // The Mach receive right is waited on by both WaitableEvent and
190*6777b538SAndroid Build Coastguard Worker   // WaitableEventWatcher. It is valid to signal and then delete an event, and
191*6777b538SAndroid Build Coastguard Worker   // a watcher should still be notified. If the right were to be destroyed
192*6777b538SAndroid Build Coastguard Worker   // immediately, the watcher would not receive the signal. Because Mach
193*6777b538SAndroid Build Coastguard Worker   // receive rights cannot have a user refcount greater than one, the right
194*6777b538SAndroid Build Coastguard Worker   // must be reference-counted manually.
195*6777b538SAndroid Build Coastguard Worker   class ReceiveRight : public RefCountedThreadSafe<ReceiveRight> {
196*6777b538SAndroid Build Coastguard Worker    public:
197*6777b538SAndroid Build Coastguard Worker     explicit ReceiveRight(mach_port_t name);
198*6777b538SAndroid Build Coastguard Worker 
199*6777b538SAndroid Build Coastguard Worker     ReceiveRight(const ReceiveRight&) = delete;
200*6777b538SAndroid Build Coastguard Worker     ReceiveRight& operator=(const ReceiveRight&) = delete;
201*6777b538SAndroid Build Coastguard Worker 
Name()202*6777b538SAndroid Build Coastguard Worker     mach_port_t Name() const { return right_.get(); }
203*6777b538SAndroid Build Coastguard Worker 
204*6777b538SAndroid Build Coastguard Worker    private:
205*6777b538SAndroid Build Coastguard Worker     friend class RefCountedThreadSafe<ReceiveRight>;
206*6777b538SAndroid Build Coastguard Worker     ~ReceiveRight();
207*6777b538SAndroid Build Coastguard Worker 
208*6777b538SAndroid Build Coastguard Worker     apple::ScopedMachReceiveRight right_;
209*6777b538SAndroid Build Coastguard Worker   };
210*6777b538SAndroid Build Coastguard Worker 
211*6777b538SAndroid Build Coastguard Worker   const ResetPolicy policy_;
212*6777b538SAndroid Build Coastguard Worker 
213*6777b538SAndroid Build Coastguard Worker   // The receive right for the event.
214*6777b538SAndroid Build Coastguard Worker   scoped_refptr<ReceiveRight> receive_right_;
215*6777b538SAndroid Build Coastguard Worker 
216*6777b538SAndroid Build Coastguard Worker   // The send right used to signal the event. This can be disposed of with
217*6777b538SAndroid Build Coastguard Worker   // the event, unlike the receive right, since a deleted event cannot be
218*6777b538SAndroid Build Coastguard Worker   // signaled.
219*6777b538SAndroid Build Coastguard Worker   apple::ScopedMachSendRight send_right_;
220*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
221*6777b538SAndroid Build Coastguard Worker   // On Windows, you must not close a HANDLE which is currently being waited on.
222*6777b538SAndroid Build Coastguard Worker   // The MSDN documentation says that the resulting behaviour is 'undefined'.
223*6777b538SAndroid Build Coastguard Worker   // To solve that issue each WaitableEventWatcher duplicates the given event
224*6777b538SAndroid Build Coastguard Worker   // handle.
225*6777b538SAndroid Build Coastguard Worker 
226*6777b538SAndroid Build Coastguard Worker   // However, if we were to include the following members
227*6777b538SAndroid Build Coastguard Worker   // directly then, on POSIX, one couldn't use WaitableEventWatcher to watch an
228*6777b538SAndroid Build Coastguard Worker   // event which gets deleted. This mismatch has bitten us several times now,
229*6777b538SAndroid Build Coastguard Worker   // so we have a kernel of the WaitableEvent, which is reference counted.
230*6777b538SAndroid Build Coastguard Worker   // WaitableEventWatchers may then take a reference and thus match the Windows
231*6777b538SAndroid Build Coastguard Worker   // behaviour.
232*6777b538SAndroid Build Coastguard Worker   struct WaitableEventKernel :
233*6777b538SAndroid Build Coastguard Worker       public RefCountedThreadSafe<WaitableEventKernel> {
234*6777b538SAndroid Build Coastguard Worker    public:
235*6777b538SAndroid Build Coastguard Worker     WaitableEventKernel(ResetPolicy reset_policy, InitialState initial_state);
236*6777b538SAndroid Build Coastguard Worker 
237*6777b538SAndroid Build Coastguard Worker     bool Dequeue(Waiter* waiter, void* tag);
238*6777b538SAndroid Build Coastguard Worker 
239*6777b538SAndroid Build Coastguard Worker     base::Lock lock_;
240*6777b538SAndroid Build Coastguard Worker     const bool manual_reset_;
241*6777b538SAndroid Build Coastguard Worker     bool signaled_;
242*6777b538SAndroid Build Coastguard Worker     std::list<raw_ptr<Waiter, CtnExperimental>> waiters_;
243*6777b538SAndroid Build Coastguard Worker 
244*6777b538SAndroid Build Coastguard Worker    private:
245*6777b538SAndroid Build Coastguard Worker     friend class RefCountedThreadSafe<WaitableEventKernel>;
246*6777b538SAndroid Build Coastguard Worker     ~WaitableEventKernel();
247*6777b538SAndroid Build Coastguard Worker   };
248*6777b538SAndroid Build Coastguard Worker 
249*6777b538SAndroid Build Coastguard Worker   typedef std::pair<WaitableEvent*, size_t> WaiterAndIndex;
250*6777b538SAndroid Build Coastguard Worker 
251*6777b538SAndroid Build Coastguard Worker   // When dealing with arrays of WaitableEvent*, we want to sort by the address
252*6777b538SAndroid Build Coastguard Worker   // of the WaitableEvent in order to have a globally consistent locking order.
253*6777b538SAndroid Build Coastguard Worker   // In that case we keep them, in sorted order, in an array of pairs where the
254*6777b538SAndroid Build Coastguard Worker   // second element is the index of the WaitableEvent in the original,
255*6777b538SAndroid Build Coastguard Worker   // unsorted, array.
256*6777b538SAndroid Build Coastguard Worker   static size_t EnqueueMany(WaiterAndIndex* waitables,
257*6777b538SAndroid Build Coastguard Worker                             size_t count, Waiter* waiter);
258*6777b538SAndroid Build Coastguard Worker 
259*6777b538SAndroid Build Coastguard Worker   bool SignalAll();
260*6777b538SAndroid Build Coastguard Worker   bool SignalOne();
261*6777b538SAndroid Build Coastguard Worker   void Enqueue(Waiter* waiter);
262*6777b538SAndroid Build Coastguard Worker 
263*6777b538SAndroid Build Coastguard Worker   scoped_refptr<WaitableEventKernel> kernel_;
264*6777b538SAndroid Build Coastguard Worker #endif
265*6777b538SAndroid Build Coastguard Worker 
266*6777b538SAndroid Build Coastguard Worker   // Whether a thread invoking Wait() on this WaitableEvent should be considered
267*6777b538SAndroid Build Coastguard Worker   // blocked as opposed to idle (and potentially replaced if part of a pool),
268*6777b538SAndroid Build Coastguard Worker   // and whether WaitableEvent should emit a wakeup.flow event on Signal =>
269*6777b538SAndroid Build Coastguard Worker   // TimedWait.
270*6777b538SAndroid Build Coastguard Worker   bool only_used_while_idle_ = false;
271*6777b538SAndroid Build Coastguard Worker };
272*6777b538SAndroid Build Coastguard Worker 
273*6777b538SAndroid Build Coastguard Worker }  // namespace base
274*6777b538SAndroid Build Coastguard Worker 
275*6777b538SAndroid Build Coastguard Worker #endif  // BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_
276