[SDK] One step further towards ReactOS source code tree restructure: the sdk folder...
[reactos.git] / reactos / sdk / lib / rtl / wait.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Rtl user wait functions
5 * FILE: lib/rtl/wait.c
6 * PROGRAMERS:
7 * Alex Ionescu (alex@relsoft.net)
8 * Eric Kohl
9 * KJK::Hyperion
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <rtl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 typedef struct _RTLP_WAIT
20 {
21 HANDLE Object;
22 BOOLEAN CallbackInProgress;
23 HANDLE CancelEvent;
24 LONG DeleteCount;
25 HANDLE CompletionEvent;
26 ULONG Flags;
27 WAITORTIMERCALLBACKFUNC Callback;
28 PVOID Context;
29 ULONG Milliseconds;
30 } RTLP_WAIT, *PRTLP_WAIT;
31
32 /* PRIVATE FUNCTIONS *******************************************************/
33
34 static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout )
35 {
36 if (timeout == INFINITE) return NULL;
37 pTime->QuadPart = (ULONGLONG)timeout * -10000;
38 return pTime;
39 }
40
41 static VOID
42 NTAPI
43 Wait_thread_proc(LPVOID Arg)
44 {
45 PRTLP_WAIT Wait = (PRTLP_WAIT) Arg;
46 NTSTATUS Status;
47 BOOLEAN alertable = (Wait->Flags & WT_EXECUTEINIOTHREAD) != 0;
48 HANDLE handles[2] = { Wait->Object, Wait->CancelEvent };
49 LARGE_INTEGER timeout;
50 HANDLE completion_event;
51
52 // TRACE("\n");
53
54 while (TRUE)
55 {
56 Status = NtWaitForMultipleObjects( 2,
57 handles,
58 WaitAny,
59 alertable,
60 get_nt_timeout( &timeout, Wait->Milliseconds ) );
61
62 if (Status == STATUS_WAIT_0 || Status == STATUS_TIMEOUT)
63 {
64 BOOLEAN TimerOrWaitFired;
65
66 if (Status == STATUS_WAIT_0)
67 {
68 // TRACE( "object %p signaled, calling callback %p with context %p\n",
69 // Wait->Object, Wait->Callback,
70 // Wait->Context );
71 TimerOrWaitFired = FALSE;
72 }
73 else
74 {
75 // TRACE( "wait for object %p timed out, calling callback %p with context %p\n",
76 // Wait->Object, Wait->Callback,
77 // Wait->Context );
78 TimerOrWaitFired = TRUE;
79 }
80 Wait->CallbackInProgress = TRUE;
81 Wait->Callback( Wait->Context, TimerOrWaitFired );
82 Wait->CallbackInProgress = FALSE;
83
84 if (Wait->Flags & WT_EXECUTEONLYONCE)
85 break;
86 }
87 else if (Status != STATUS_USER_APC)
88 break;
89 }
90
91 completion_event = Wait->CompletionEvent;
92 if (completion_event) NtSetEvent( completion_event, NULL );
93
94 if (InterlockedIncrement( &Wait->DeleteCount ) == 2 )
95 {
96 NtClose( Wait->CancelEvent );
97 RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
98 }
99 }
100
101
102 /* FUNCTIONS ***************************************************************/
103
104
105 /***********************************************************************
106 * RtlRegisterWait
107 *
108 * Registers a wait for a handle to become signaled.
109 *
110 * PARAMS
111 * NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it.
112 * Object [I] Object to wait to become signaled.
113 * Callback [I] Callback function to execute when the wait times out or the handle is signaled.
114 * Context [I] Context to pass to the callback function when it is executed.
115 * Milliseconds [I] Number of milliseconds to wait before timing out.
116 * Flags [I] Flags. See notes.
117 *
118 * RETURNS
119 * Success: STATUS_SUCCESS.
120 * Failure: Any NTSTATUS code.
121 *
122 * NOTES
123 * Flags can be one or more of the following:
124 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
125 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
126 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
127 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
128 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
129 */
130 NTSTATUS
131 NTAPI
132 RtlRegisterWait(PHANDLE NewWaitObject,
133 HANDLE Object,
134 WAITORTIMERCALLBACKFUNC Callback,
135 PVOID Context,
136 ULONG Milliseconds,
137 ULONG Flags)
138 {
139 PRTLP_WAIT Wait;
140 NTSTATUS Status;
141
142 //TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags );
143
144 Wait = RtlAllocateHeap( RtlGetProcessHeap(), 0, sizeof(RTLP_WAIT) );
145 if (!Wait)
146 return STATUS_NO_MEMORY;
147
148 Wait->Object = Object;
149 Wait->Callback = Callback;
150 Wait->Context = Context;
151 Wait->Milliseconds = Milliseconds;
152 Wait->Flags = Flags;
153 Wait->CallbackInProgress = FALSE;
154 Wait->DeleteCount = 0;
155 Wait->CompletionEvent = NULL;
156
157 Status = NtCreateEvent( &Wait->CancelEvent,
158 EVENT_ALL_ACCESS,
159 NULL,
160 NotificationEvent,
161 FALSE );
162
163 if (Status != STATUS_SUCCESS)
164 {
165 RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
166 return Status;
167 }
168
169 Flags = Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD |
170 WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION);
171
172 Status = RtlQueueWorkItem( Wait_thread_proc,
173 Wait,
174 Flags );
175
176 if (Status != STATUS_SUCCESS)
177 {
178 NtClose( Wait->CancelEvent );
179 RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
180 return Status;
181 }
182
183 *NewWaitObject = Wait;
184 return Status;
185 }
186
187 /***********************************************************************
188 * RtlDeregisterWaitEx
189 *
190 * Cancels a wait operation and frees the resources associated with calling
191 * RtlRegisterWait().
192 *
193 * PARAMS
194 * WaitObject [I] Handle to the wait object to free.
195 *
196 * RETURNS
197 * Success: STATUS_SUCCESS.
198 * Failure: Any NTSTATUS code.
199 */
200 NTSTATUS
201 NTAPI
202 RtlDeregisterWaitEx(HANDLE WaitHandle,
203 HANDLE CompletionEvent)
204 {
205 PRTLP_WAIT Wait = (PRTLP_WAIT) WaitHandle;
206 NTSTATUS Status = STATUS_SUCCESS;
207
208 //TRACE( "(%p)\n", WaitHandle );
209
210 NtSetEvent( Wait->CancelEvent, NULL );
211 if (Wait->CallbackInProgress)
212 {
213 if (CompletionEvent != NULL)
214 {
215 if (CompletionEvent == INVALID_HANDLE_VALUE)
216 {
217 Status = NtCreateEvent( &CompletionEvent,
218 EVENT_ALL_ACCESS,
219 NULL,
220 NotificationEvent,
221 FALSE );
222
223 if (Status != STATUS_SUCCESS)
224 return Status;
225
226 (void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent );
227
228 if (Wait->CallbackInProgress)
229 NtWaitForSingleObject( CompletionEvent, FALSE, NULL );
230
231 NtClose( CompletionEvent );
232 }
233 else
234 {
235 (void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent );
236
237 if (Wait->CallbackInProgress)
238 Status = STATUS_PENDING;
239 }
240 }
241 else
242 Status = STATUS_PENDING;
243 }
244
245 if (InterlockedIncrement( &Wait->DeleteCount ) == 2 )
246 {
247 Status = STATUS_SUCCESS;
248 NtClose( Wait->CancelEvent );
249 RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
250 }
251
252 return Status;
253 }
254
255 /***********************************************************************
256 * RtlDeregisterWait
257 *
258 * Cancels a wait operation and frees the resources associated with calling
259 * RtlRegisterWait().
260 *
261 * PARAMS
262 * WaitObject [I] Handle to the wait object to free.
263 *
264 * RETURNS
265 * Success: STATUS_SUCCESS.
266 * Failure: Any NTSTATUS code.
267 */
268 NTSTATUS
269 NTAPI
270 RtlDeregisterWait(HANDLE WaitHandle)
271 {
272 return RtlDeregisterWaitEx(WaitHandle, NULL);
273 }
274
275 /* EOF */