xref: /reactos/sdk/lib/rtl/wait.c (revision ea6e7740)
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->CancelEvent, Wait->Object };
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_1 || Status == STATUS_TIMEOUT)
63         {
64             BOOLEAN TimerOrWaitFired;
65 
66             if (Status == STATUS_WAIT_1)
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 */
276