ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

timerqueue.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.