1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxWaitLock.hpp
8 
9 Abstract:
10 
11 --*/
12 
13 #ifndef _FXWAITLOCK_HPP_
14 #define _FXWAITLOCK_HPP_
15 
16 struct FxCREvent {
17     FxCREvent(
18         __in BOOLEAN InitialState = FALSE
19         )
20     {
21     //
22     // For kernel mode letting c'tor do the initialization to not churn the
23     // non-shared code
24     //
25 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
26         m_Event.Initialize(SynchronizationEvent, InitialState);
27 #else
28         UNREFERENCED_PARAMETER(InitialState);
29 #endif
30     }
31 
32 
33 
34 
35 
36     FxCREvent(
37         __in EVENT_TYPE Type,
38         __in BOOLEAN InitialState
39         )
40     {
41 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
42         m_Event.Initialize(Type, InitialState);
43 #else
44         UNREFERENCED_PARAMETER(Type);
45         UNREFERENCED_PARAMETER(InitialState);
46 #endif
47     }
48 
49     CHECK_RETURN_IF_USER_MODE
50     NTSTATUS
51     Initialize(
52         __in BOOLEAN InitialState = FALSE
53         )
54     {
55         return m_Event.Initialize(SynchronizationEvent, InitialState);
56     }
57 
58     CHECK_RETURN_IF_USER_MODE
59     NTSTATUS
60     Initialize(
61         __in EVENT_TYPE Type,
62         __in BOOLEAN InitialState
63         )
64     {
65         return m_Event.Initialize(Type, InitialState);
66     }
67 
68     __drv_valueIs(==0;==258)
69     _Acquires_lock_(_Global_critical_region_)
70     NTSTATUS
71     EnterCRAndWait(
72         VOID
73         )
74     {
75         NTSTATUS status;
76 
77         Mx::MxEnterCriticalRegion();
78 
79         status = m_Event.WaitFor(Executive,
80                                  KernelMode,
81                                  FALSE,
82                                  NULL);
83         return status;
84     }
85 
86     NTSTATUS
87     EnterCRAndWaitAndLeave(
88         VOID
89         )
90     {
91         NTSTATUS status;
92 
93         status = EnterCRAndWait();
94         LeaveCR();
95 
96         return status;
97     }
98 
99     __drv_when(Timeout == NULL, __drv_valueIs(==0))
100     __drv_when(Timeout != NULL, __drv_valueIs(==0;==258))
101     __drv_when(Timeout != NULL, _Must_inspect_result_)
102     _Acquires_lock_(_Global_critical_region_)
103     NTSTATUS
104     EnterCRAndWait(
105         __in PLONGLONG Timeout
106         )
107     {
108         NTSTATUS status;
109 
110         Mx::MxEnterCriticalRegion();
111 
112         status = m_Event.WaitFor(Executive,
113                                  KernelMode,
114                                  FALSE,
115                                  (PLARGE_INTEGER) Timeout);
116 
117         return status;
118     }
119 
120     _Must_inspect_result_
121     NTSTATUS
122     EnterCRAndWaitAndLeave(
123         __in PLONGLONG Timeout
124         )
125     {
126         NTSTATUS status;
127 
128         status = EnterCRAndWait(Timeout);
129         LeaveCR();
130 
131         return status;
132     }
133 
134     _Releases_lock_(_Global_critical_region_)
135     VOID
136     LeaveCR(
137         VOID
138         )
139     {
140         Mx::MxLeaveCriticalRegion();
141     }
142 
143     VOID
144     Set(
145         VOID
146         )
147     {
148         m_Event.Set();
149     }
150 
151     VOID
152     Clear(
153         VOID
154         )
155     {
156         m_Event.Clear();
157     }
158 
159     LONG
160     ReadState(
161         VOID
162         )
163     {
164         return m_Event.ReadState();
165     }
166 
167     //
168     // Return the underlying event
169     // PKEVENT in kernel mode and event HANDLE in user-mode
170     //
171     PVOID
172     GetEvent(
173         VOID
174         )
175     {
176         return m_Event.GetEvent();
177     }
178 
179     FxCREvent*
180     GetSelfPointer(
181         VOID
182         )
183     {
184         //
185         // Since operator& is hidden, we still need to be able to get a pointer
186         // to this object, so we must make it an explicit method.
187         //
188         return this;
189     }
190 private:
191     FxCREvent* operator&(
192         VOID
193         )
194     {
195         //
196         // By making the address of operator private, we make it harder (hopefully
197         // impossible) to accidentally use this object in an improper fashion, ie
198         // something like this is prevented:
199         //
200         // FxCREvent event;
201         // KeWaitForSingleObject(&event, ...);
202         //
203         ASSERT(FALSE);
204         return NULL;
205     }
206 
207 private:
208     MxEvent m_Event;
209 };
210 
211 //
212 // Non referencable object, just pure implementation
213 //
214 class FxWaitLockInternal {
215 
216 public:
217 
218     FxWaitLockInternal(
219         VOID
220         )
221     {
222     //
223     // For kernel mode letting c'tor do the initialization to not churn the
224     // non-shared code
225     //
226 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
227         m_Event.Initialize(SynchronizationEvent, TRUE);
228 #endif
229 
230         m_OwningThread = NULL;
231     }
232 
233     CHECK_RETURN_IF_USER_MODE
234     NTSTATUS
235     Initialize(
236         )
237     {
238         return m_Event.Initialize(SynchronizationEvent, TRUE);
239     }
240 
241     //
242 
243     //
244     __drv_when(Timeout == NULL, __drv_valueIs(==0))
245     __drv_when(Timeout != NULL, __drv_valueIs(==0;==258))
246     __drv_when(Timeout != NULL, _Must_inspect_result_)
247     _When_(return!=0x00000102L, _Acquires_lock_(_Global_critical_region_))
248     NTSTATUS
249     AcquireLock(
250         __in        PFX_DRIVER_GLOBALS FxDriverGlobals,
251         __in_opt    PLONGLONG Timeout = NULL
252         )
253     {
254         LARGE_INTEGER li;
255         NTSTATUS status;
256 
257         UNREFERENCED_PARAMETER(FxDriverGlobals);
258 
259         ASSERT(m_OwningThread != Mx::MxGetCurrentThread() || (Timeout != NULL));
260 
261         if (Timeout != NULL) {
262             li.QuadPart = *Timeout;
263         }
264 
265         Mx::MxEnterCriticalRegion();
266         status = m_Event.WaitFor(Executive,
267                                  KernelMode,
268                                  FALSE,
269                                  Timeout == NULL ? NULL : &li);
270 
271         if (status == STATUS_TIMEOUT) {
272             Mx::MxLeaveCriticalRegion();
273         }
274         else {
275             m_OwningThread = Mx::MxGetCurrentThread();
276         }
277 
278         return status;
279     }
280 
281     static
282     BOOLEAN
283     IsLockAcquired(
284         __in NTSTATUS Status
285         )
286     {
287         //
288         // STATUS_TIMEOUT will return TRUE for NT_SUCCESS so check explicitly
289         //
290         return (NT_SUCCESS(Status) && Status != STATUS_TIMEOUT) ? TRUE : FALSE;
291     }
292 
293     _Releases_lock_(_Global_critical_region_)
294     VOID
295     ReleaseLock(
296         __in PFX_DRIVER_GLOBALS FxDriverGlobals
297         )
298     {
299         UNREFERENCED_PARAMETER(FxDriverGlobals);
300 
301         ASSERT(m_OwningThread == Mx::MxGetCurrentThread());
302         m_OwningThread = NULL;
303 
304         m_Event.Set();
305         Mx::MxLeaveCriticalRegion();
306     }
307 
308 protected:
309     MxEvent m_Event;
310 
311     MxThread m_OwningThread;
312 };
313 
314 
315 //
316 // Order is important here, FxObject *must* be the first class in the
317 // list so that &FxWaitWaitLock == &FxNonPagedObject.
318 //
319 class FxWaitLock : public FxObject, public FxWaitLockInternal  {
320 
321 public:
322     // Factory function
323     _Must_inspect_result_
324     static
325     NTSTATUS
326     _Create(
327         __in PFX_DRIVER_GLOBALS         DriverGlobals,
328         __in_opt PWDF_OBJECT_ATTRIBUTES Attributes,
329         __in_opt FxObject*              ParentObject,
330         __in BOOLEAN                    AssignDriverAsDefaultParent,
331         __out WDFWAITLOCK*              LockHandle
332         );
333 
334 
335     CHECK_RETURN_IF_USER_MODE
336     NTSTATUS
337     Initialize(
338         )
339     {
340         return FxWaitLockInternal::Initialize(); // __super call
341     }
342 
343     FxWaitLock(
344         __in PFX_DRIVER_GLOBALS FxDriverGlobals
345         ) :
346         FxObject(FX_TYPE_WAIT_LOCK, sizeof(FxWaitLock), FxDriverGlobals)
347     {
348     }
349 };
350 
351 #endif // _FXWAITLOCK_HPP_
352