Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentimerqueue.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS system libraries 00004 * PURPOSE: Timer Queue implementation 00005 * FILE: lib/rtl/timerqueue.c 00006 * PROGRAMMER: 00007 */ 00008 00009 /* INCLUDES *****************************************************************/ 00010 00011 #include <rtl.h> 00012 00013 #define NDEBUG 00014 #include <debug.h> 00015 00016 #undef LIST_FOR_EACH 00017 #undef LIST_FOR_EACH_SAFE 00018 #include <wine/list.h> 00019 00020 /* FUNCTIONS ***************************************************************/ 00021 00022 extern PRTL_START_POOL_THREAD RtlpStartThreadFunc; 00023 extern PRTL_EXIT_POOL_THREAD RtlpExitThreadFunc; 00024 HANDLE TimerThreadHandle = NULL; 00025 00026 NTSTATUS 00027 RtlpInitializeTimerThread(VOID) 00028 { 00029 return STATUS_NOT_IMPLEMENTED; 00030 } 00031 00032 static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout ) 00033 { 00034 if (timeout == INFINITE) return NULL; 00035 pTime->QuadPart = (ULONGLONG)timeout * -10000; 00036 return pTime; 00037 } 00038 00039 struct timer_queue; 00040 struct queue_timer 00041 { 00042 struct timer_queue *q; 00043 struct list entry; 00044 ULONG runcount; /* number of callbacks pending execution */ 00045 WAITORTIMERCALLBACKFUNC callback; 00046 PVOID param; 00047 DWORD period; 00048 ULONG flags; 00049 ULONGLONG expire; 00050 BOOL destroy; /* timer should be deleted; once set, never unset */ 00051 HANDLE event; /* removal event */ 00052 }; 00053 00054 struct timer_queue 00055 { 00056 RTL_CRITICAL_SECTION cs; 00057 struct list timers; /* sorted by expiration time */ 00058 BOOL quit; /* queue should be deleted; once set, never unset */ 00059 HANDLE event; 00060 HANDLE thread; 00061 }; 00062 00063 #define EXPIRE_NEVER (~(ULONGLONG) 0) 00064 00065 static void queue_remove_timer(struct queue_timer *t) 00066 { 00067 /* We MUST hold the queue cs while calling this function. This ensures 00068 that we cannot queue another callback for this timer. The runcount 00069 being zero makes sure we don't have any already queued. */ 00070 struct timer_queue *q = t->q; 00071 00072 assert(t->runcount == 0); 00073 assert(t->destroy); 00074 00075 list_remove(&t->entry); 00076 if (t->event) 00077 NtSetEvent(t->event, NULL); 00078 RtlFreeHeap(RtlGetProcessHeap(), 0, t); 00079 00080 if (q->quit && list_count(&q->timers) == 0) 00081 NtSetEvent(q->event, NULL); 00082 } 00083 00084 static void timer_cleanup_callback(struct queue_timer *t) 00085 { 00086 struct timer_queue *q = t->q; 00087 RtlEnterCriticalSection(&q->cs); 00088 00089 assert(0 < t->runcount); 00090 --t->runcount; 00091 00092 if (t->destroy && t->runcount == 0) 00093 queue_remove_timer(t); 00094 00095 RtlLeaveCriticalSection(&q->cs); 00096 } 00097 00098 static DWORD WINAPI timer_callback_wrapper(LPVOID p) 00099 { 00100 struct queue_timer *t = p; 00101 t->callback(t->param, TRUE); 00102 timer_cleanup_callback(t); 00103 return 0; 00104 } 00105 00106 static inline ULONGLONG queue_current_time(void) 00107 { 00108 LARGE_INTEGER now; 00109 NtQuerySystemTime(&now); 00110 return now.QuadPart / 10000; 00111 } 00112 00113 static void queue_add_timer(struct queue_timer *t, ULONGLONG time, 00114 BOOL set_event) 00115 { 00116 /* We MUST hold the queue cs while calling this function. */ 00117 struct timer_queue *q = t->q; 00118 struct list *ptr = &q->timers; 00119 00120 assert(!q->quit || (t->destroy && time == EXPIRE_NEVER)); 00121 00122 if (time != EXPIRE_NEVER) 00123 LIST_FOR_EACH(ptr, &q->timers) 00124 { 00125 struct queue_timer *cur = LIST_ENTRY(ptr, struct queue_timer, entry); 00126 if (time < cur->expire) 00127 break; 00128 } 00129 list_add_before(ptr, &t->entry); 00130 00131 t->expire = time; 00132 00133 /* If we insert at the head of the list, we need to expire sooner 00134 than expected. */ 00135 if (set_event && &t->entry == list_head(&q->timers)) 00136 NtSetEvent(q->event, NULL); 00137 } 00138 00139 static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time, 00140 BOOL set_event) 00141 { 00142 /* We MUST hold the queue cs while calling this function. */ 00143 list_remove(&t->entry); 00144 queue_add_timer(t, time, set_event); 00145 } 00146 00147 static void queue_timer_expire(struct timer_queue *q) 00148 { 00149 struct queue_timer *t = NULL; 00150 00151 RtlEnterCriticalSection(&q->cs); 00152 if (list_head(&q->timers)) 00153 { 00154 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry); 00155 if (!t->destroy && t->expire <= queue_current_time()) 00156 { 00157 ++t->runcount; 00158 queue_move_timer( 00159 t, t->period ? queue_current_time() + t->period : EXPIRE_NEVER, 00160 FALSE); 00161 } 00162 else 00163 t = NULL; 00164 } 00165 RtlLeaveCriticalSection(&q->cs); 00166 00167 if (t) 00168 { 00169 if (t->flags & WT_EXECUTEINTIMERTHREAD) 00170 timer_callback_wrapper(t); 00171 else 00172 { 00173 ULONG flags 00174 = (t->flags 00175 & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD 00176 | WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION)); 00177 NTSTATUS status = RtlQueueWorkItem((WORKERCALLBACKFUNC)timer_callback_wrapper, t, flags); 00178 if (status != STATUS_SUCCESS) 00179 timer_cleanup_callback(t); 00180 } 00181 } 00182 } 00183 00184 static ULONG queue_get_timeout(struct timer_queue *q) 00185 { 00186 struct queue_timer *t; 00187 ULONG timeout = INFINITE; 00188 00189 RtlEnterCriticalSection(&q->cs); 00190 if (list_head(&q->timers)) 00191 { 00192 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry); 00193 assert(!t->destroy || t->expire == EXPIRE_NEVER); 00194 00195 if (t->expire != EXPIRE_NEVER) 00196 { 00197 ULONGLONG time = queue_current_time(); 00198 timeout = t->expire < time ? 0 : (ULONG)(t->expire - time); 00199 } 00200 } 00201 RtlLeaveCriticalSection(&q->cs); 00202 00203 return timeout; 00204 } 00205 00206 static void WINAPI timer_queue_thread_proc(LPVOID p) 00207 { 00208 struct timer_queue *q = p; 00209 ULONG timeout_ms; 00210 00211 timeout_ms = INFINITE; 00212 for (;;) 00213 { 00214 LARGE_INTEGER timeout; 00215 NTSTATUS status; 00216 BOOL done = FALSE; 00217 00218 status = NtWaitForSingleObject( 00219 q->event, FALSE, get_nt_timeout(&timeout, timeout_ms)); 00220 00221 if (status == STATUS_WAIT_0) 00222 { 00223 /* There are two possible ways to trigger the event. Either 00224 we are quitting and the last timer got removed, or a new 00225 timer got put at the head of the list so we need to adjust 00226 our timeout. */ 00227 RtlEnterCriticalSection(&q->cs); 00228 if (q->quit && list_count(&q->timers) == 0) 00229 done = TRUE; 00230 RtlLeaveCriticalSection(&q->cs); 00231 } 00232 else if (status == STATUS_TIMEOUT) 00233 queue_timer_expire(q); 00234 00235 if (done) 00236 break; 00237 00238 timeout_ms = queue_get_timeout(q); 00239 } 00240 00241 NtClose(q->event); 00242 RtlDeleteCriticalSection(&q->cs); 00243 RtlFreeHeap(RtlGetProcessHeap(), 0, q); 00244 RtlpExitThreadFunc(STATUS_SUCCESS); 00245 } 00246 00247 static void queue_destroy_timer(struct queue_timer *t) 00248 { 00249 /* We MUST hold the queue cs while calling this function. */ 00250 t->destroy = TRUE; 00251 if (t->runcount == 0) 00252 /* Ensure a timer is promptly removed. If callbacks are pending, 00253 it will be removed after the last one finishes by the callback 00254 cleanup wrapper. */ 00255 queue_remove_timer(t); 00256 else 00257 /* Make sure no destroyed timer masks an active timer at the head 00258 of the sorted list. */ 00259 queue_move_timer(t, EXPIRE_NEVER, FALSE); 00260 } 00261 00262 /*********************************************************************** 00263 * RtlCreateTimerQueue (NTDLL.@) 00264 * 00265 * Creates a timer queue object and returns a handle to it. 00266 * 00267 * PARAMS 00268 * NewTimerQueue [O] The newly created queue. 00269 * 00270 * RETURNS 00271 * Success: STATUS_SUCCESS. 00272 * Failure: Any NTSTATUS code. 00273 */ 00274 NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue) 00275 { 00276 NTSTATUS status; 00277 struct timer_queue *q = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *q); 00278 if (!q) 00279 return STATUS_NO_MEMORY; 00280 00281 RtlInitializeCriticalSection(&q->cs); 00282 list_init(&q->timers); 00283 q->quit = FALSE; 00284 status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, FALSE, FALSE); 00285 if (status != STATUS_SUCCESS) 00286 { 00287 RtlFreeHeap(RtlGetProcessHeap(), 0, q); 00288 return status; 00289 } 00290 status = RtlpStartThreadFunc((PVOID)timer_queue_thread_proc, q, &q->thread); 00291 if (status != STATUS_SUCCESS) 00292 { 00293 NtClose(q->event); 00294 RtlFreeHeap(RtlGetProcessHeap(), 0, q); 00295 return status; 00296 } 00297 00298 NtResumeThread(q->thread, NULL); 00299 *NewTimerQueue = q; 00300 return STATUS_SUCCESS; 00301 } 00302 00303 /*********************************************************************** 00304 * RtlDeleteTimerQueueEx (NTDLL.@) 00305 * 00306 * Deletes a timer queue object. 00307 * 00308 * PARAMS 00309 * TimerQueue [I] The timer queue to destroy. 00310 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE, 00311 * wait until all timers are finished firing before 00312 * returning. Otherwise, return immediately and set the 00313 * event when all timers are done. 00314 * 00315 * RETURNS 00316 * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not. 00317 * Failure: Any NTSTATUS code. 00318 */ 00319 NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) 00320 { 00321 struct timer_queue *q = TimerQueue; 00322 struct queue_timer *t, *temp; 00323 HANDLE thread; 00324 NTSTATUS status; 00325 00326 if (!q) 00327 return STATUS_INVALID_HANDLE; 00328 00329 thread = q->thread; 00330 00331 RtlEnterCriticalSection(&q->cs); 00332 q->quit = TRUE; 00333 if (list_head(&q->timers)) 00334 /* When the last timer is removed, it will signal the timer thread to 00335 exit... */ 00336 LIST_FOR_EACH_ENTRY_SAFE(t, temp, &q->timers, struct queue_timer, entry) 00337 queue_destroy_timer(t); 00338 else 00339 /* However if we have none, we must do it ourselves. */ 00340 NtSetEvent(q->event, NULL); 00341 RtlLeaveCriticalSection(&q->cs); 00342 00343 if (CompletionEvent == INVALID_HANDLE_VALUE) 00344 { 00345 NtWaitForSingleObject(thread, FALSE, NULL); 00346 status = STATUS_SUCCESS; 00347 } 00348 else 00349 { 00350 if (CompletionEvent) 00351 { 00352 DPRINT1("asynchronous return on completion event unimplemented\n"); 00353 NtWaitForSingleObject(thread, FALSE, NULL); 00354 NtSetEvent(CompletionEvent, NULL); 00355 } 00356 status = STATUS_PENDING; 00357 } 00358 00359 NtClose(thread); 00360 return status; 00361 } 00362 00363 static struct timer_queue *default_timer_queue; 00364 00365 static struct timer_queue *get_timer_queue(HANDLE TimerQueue) 00366 { 00367 if (TimerQueue) 00368 return TimerQueue; 00369 else 00370 { 00371 if (!default_timer_queue) 00372 { 00373 HANDLE q; 00374 NTSTATUS status = RtlCreateTimerQueue(&q); 00375 if (status == STATUS_SUCCESS) 00376 { 00377 PVOID p = InterlockedCompareExchangePointer( 00378 (void **) &default_timer_queue, q, NULL); 00379 if (p) 00380 /* Got beat to the punch. */ 00381 RtlDeleteTimerQueueEx(p, NULL); 00382 } 00383 } 00384 return default_timer_queue; 00385 } 00386 } 00387 00388 /*********************************************************************** 00389 * RtlCreateTimer (NTDLL.@) 00390 * 00391 * Creates a new timer associated with the given queue. 00392 * 00393 * PARAMS 00394 * NewTimer [O] The newly created timer. 00395 * TimerQueue [I] The queue to hold the timer. 00396 * Callback [I] The callback to fire. 00397 * Parameter [I] The argument for the callback. 00398 * DueTime [I] The delay, in milliseconds, before first firing the 00399 * timer. 00400 * Period [I] The period, in milliseconds, at which to fire the timer 00401 * after the first callback. If zero, the timer will only 00402 * fire once. It still needs to be deleted with 00403 * RtlDeleteTimer. 00404 * Flags [I] Flags controling the execution of the callback. In 00405 * addition to the WT_* thread pool flags (see 00406 * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and 00407 * WT_EXECUTEONLYONCE are supported. 00408 * 00409 * RETURNS 00410 * Success: STATUS_SUCCESS. 00411 * Failure: Any NTSTATUS code. 00412 */ 00413 NTSTATUS WINAPI RtlCreateTimer(HANDLE TimerQueue, PHANDLE NewTimer, 00414 WAITORTIMERCALLBACKFUNC Callback, 00415 PVOID Parameter, DWORD DueTime, DWORD Period, 00416 ULONG Flags) 00417 { 00418 NTSTATUS status; 00419 struct queue_timer *t; 00420 struct timer_queue *q = get_timer_queue(TimerQueue); 00421 if (!q) 00422 return STATUS_NO_MEMORY; 00423 00424 t = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *t); 00425 if (!t) 00426 return STATUS_NO_MEMORY; 00427 00428 t->q = q; 00429 t->runcount = 0; 00430 t->callback = Callback; 00431 t->param = Parameter; 00432 t->period = Period; 00433 t->flags = Flags; 00434 t->destroy = FALSE; 00435 t->event = NULL; 00436 00437 status = STATUS_SUCCESS; 00438 RtlEnterCriticalSection(&q->cs); 00439 if (q->quit) 00440 status = STATUS_INVALID_HANDLE; 00441 else 00442 queue_add_timer(t, queue_current_time() + DueTime, TRUE); 00443 RtlLeaveCriticalSection(&q->cs); 00444 00445 if (status == STATUS_SUCCESS) 00446 *NewTimer = t; 00447 else 00448 RtlFreeHeap(RtlGetProcessHeap(), 0, t); 00449 00450 return status; 00451 } 00452 00453 /*********************************************************************** 00454 * RtlUpdateTimer (NTDLL.@) 00455 * 00456 * Changes the time at which a timer expires. 00457 * 00458 * PARAMS 00459 * TimerQueue [I] The queue that holds the timer. 00460 * Timer [I] The timer to update. 00461 * DueTime [I] The delay, in milliseconds, before next firing the timer. 00462 * Period [I] The period, in milliseconds, at which to fire the timer 00463 * after the first callback. If zero, the timer will not 00464 * refire once. It still needs to be deleted with 00465 * RtlDeleteTimer. 00466 * 00467 * RETURNS 00468 * Success: STATUS_SUCCESS. 00469 * Failure: Any NTSTATUS code. 00470 */ 00471 NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer, 00472 DWORD DueTime, DWORD Period) 00473 { 00474 struct queue_timer *t = Timer; 00475 struct timer_queue *q = t->q; 00476 00477 RtlEnterCriticalSection(&q->cs); 00478 /* Can't change a timer if it was once-only or destroyed. */ 00479 if (t->expire != EXPIRE_NEVER) 00480 { 00481 t->period = Period; 00482 queue_move_timer(t, queue_current_time() + DueTime, TRUE); 00483 } 00484 RtlLeaveCriticalSection(&q->cs); 00485 00486 return STATUS_SUCCESS; 00487 } 00488 00489 /*********************************************************************** 00490 * RtlDeleteTimer (NTDLL.@) 00491 * 00492 * Cancels a timer-queue timer. 00493 * 00494 * PARAMS 00495 * TimerQueue [I] The queue that holds the timer. 00496 * Timer [I] The timer to update. 00497 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE, 00498 * wait until the timer is finished firing all pending 00499 * callbacks before returning. Otherwise, return 00500 * immediately and set the timer is done. 00501 * 00502 * RETURNS 00503 * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not, 00504 or if the completion event is NULL. 00505 * Failure: Any NTSTATUS code. 00506 */ 00507 NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer, 00508 HANDLE CompletionEvent) 00509 { 00510 struct queue_timer *t = Timer; 00511 struct timer_queue *q; 00512 NTSTATUS status = STATUS_PENDING; 00513 HANDLE event = NULL; 00514 00515 if (!Timer) 00516 return STATUS_INVALID_PARAMETER_1; 00517 q = t->q; 00518 if (CompletionEvent == INVALID_HANDLE_VALUE) 00519 status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, FALSE, FALSE); 00520 else if (CompletionEvent) 00521 event = CompletionEvent; 00522 00523 RtlEnterCriticalSection(&q->cs); 00524 t->event = event; 00525 if (t->runcount == 0 && event) 00526 status = STATUS_SUCCESS; 00527 queue_destroy_timer(t); 00528 RtlLeaveCriticalSection(&q->cs); 00529 00530 if (CompletionEvent == INVALID_HANDLE_VALUE && event) 00531 { 00532 if (status == STATUS_PENDING) 00533 NtWaitForSingleObject(event, FALSE, NULL); 00534 NtClose(event); 00535 } 00536 00537 return status; 00538 } 00539 00540 /* 00541 * @implemented 00542 */ 00543 NTSTATUS 00544 NTAPI 00545 RtlDeleteTimerQueue(HANDLE TimerQueue) 00546 { 00547 return RtlDeleteTimerQueueEx(TimerQueue, INVALID_HANDLE_VALUE); 00548 } 00549 00550 /* EOF */ Generated on Sat May 26 2012 04:23:04 for ReactOS by
1.7.6.1
|