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