tpstimer.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 17k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4.     tpstimer.cpp
  5. Abstract:
  6.     Contains Win32 thread pool services timer functions
  7.     Contents:
  8.         TerminateTimers
  9.         SHCreateTimerQueue
  10.         SHDeleteTimerQueue
  11.         SHSetTimerQueueTimer
  12.         SHChangeTimerQueueTimer
  13.         SHCancelTimerQueueTimer
  14.         (InitializeTimerThread)
  15.         (TimerCleanup)
  16.         (CreateDefaultTimerQueue)
  17.         (DeleteDefaultTimerQueue)
  18.         (CleanupDefaultTimerQueue)
  19.         (TimerThread)
  20.         (DeleteTimerQueue)
  21.         (AddTimer)
  22.         (ChangeTimer)
  23.         (CancelTimer)
  24. Author:
  25.     Richard L Firth (rfirth) 10-Feb-1998
  26. Environment:
  27.     Win32 user-mode
  28. Notes:
  29.     Code reworked in C++ from NT-specific C code written by Gurdeep Singh Pall
  30.     (gurdeep)
  31. Revision History:
  32.     10-Feb-1998 rfirth
  33.         Created
  34. --*/
  35. #include "priv.h"
  36. #include "threads.h"
  37. #include "tpsclass.h"
  38. #include "tpstimer.h"
  39. //
  40. // private prototypes
  41. //
  42. PRIVATE
  43. DWORD
  44. InitializeTimerThread(
  45.     VOID
  46.     );
  47. PRIVATE
  48. VOID
  49. TimerCleanup(
  50.     VOID
  51.     );
  52. PRIVATE
  53. HANDLE
  54. CreateDefaultTimerQueue(
  55.     VOID
  56.     );
  57. PRIVATE
  58. VOID
  59. DeleteDefaultTimerQueue(
  60.     VOID
  61.     );
  62. PRIVATE
  63. VOID
  64. CleanupDefaultTimerQueue(
  65.     VOID
  66.     );
  67. PRIVATE
  68. VOID
  69. TimerThread(
  70.     VOID
  71.     );
  72. PRIVATE
  73. VOID
  74. DeleteTimerQueue(
  75.     IN CTimerQueueDeleteRequest * pRequest
  76.     );
  77. PRIVATE
  78. VOID
  79. AddTimer(
  80.     IN CTimerAddRequest * pRequest
  81.     );
  82. PRIVATE
  83. VOID
  84. ChangeTimer(
  85.     IN CTimerChangeRequest * pRequest
  86.     );
  87. PRIVATE
  88. VOID
  89. CancelTimer(
  90.     IN CTimerCancelRequest * pRequest
  91.     );
  92. //
  93. // global data
  94. //
  95. CTimerQueueList g_TimerQueueList;
  96. HANDLE g_hDefaultTimerQueue = NULL;
  97. HANDLE g_hTimerThread = NULL;
  98. DWORD g_dwTimerId = 0;
  99. LONG g_UID = 0;
  100. BOOL g_bTimerInit = FALSE;
  101. BOOL g_bTimerInitDone = FALSE;
  102. BOOL g_bDeferredTimerTermination = FALSE;
  103. //
  104. // functions
  105. //
  106. VOID
  107. TerminateTimers(
  108.     VOID
  109.     )
  110. /*++
  111. Routine Description:
  112.     Terminate timer thread and global variables
  113. Arguments:
  114.     None.
  115. Return Value:
  116.     None.
  117. --*/
  118. {
  119.     if (g_bTimerInitDone) {
  120.         DWORD threadId = GetCurrentThreadId();
  121.         if ((g_hTimerThread != NULL) && (threadId != g_dwTimerId)) {
  122.             QueueNullFunc(g_hTimerThread);
  123.             DWORD ticks = GetTickCount();
  124.             while (g_hTimerThread != NULL) {
  125.                 SleepEx(0, TRUE);
  126.                 if (GetTickCount() - ticks > 10000) {
  127.                     CloseHandle(g_hTimerThread);
  128.                     g_hTimerThread = NULL;
  129.                     break;
  130.                 }
  131.             }
  132.         }
  133.         if (g_dwTimerId == threadId) {
  134.             g_bDeferredTimerTermination = TRUE;
  135.         } else {
  136.             TimerCleanup();
  137.         }
  138.     }
  139. }
  140. LWSTDAPI_(HANDLE)
  141. SHCreateTimerQueue(
  142.     VOID
  143.     )
  144. /*++
  145. Routine Description:
  146.     Creates a timer queue
  147. Arguments:
  148.     None.
  149. Return Value:
  150.     HANDLE
  151.         Success - non-NULL pointer to CTimerQueue object
  152.         Failure - NULL. GetLastError() for more info
  153. --*/
  154. {
  155.     InterlockedIncrement((LPLONG)&g_ActiveRequests);
  156.     HANDLE hResult = NULL;
  157.     DWORD error = ERROR_SUCCESS;
  158.     if (!g_bTpsTerminating) {
  159.         if (g_hTimerThread == NULL) {
  160.             error = InitializeTimerThread();
  161.         }
  162.         if (error == ERROR_SUCCESS) {
  163.             //
  164.             // timer queue handle is just pointer to timer queue object
  165.             //
  166.             hResult = (HANDLE) new CTimerQueue(&g_TimerQueueList);
  167.         } else {
  168.             SetLastError(error);
  169.         }
  170.     } else {
  171.         SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // BUGBUG - error code?
  172.     }
  173.     InterlockedDecrement((LPLONG)&g_ActiveRequests);
  174.     return hResult;
  175. }
  176. LWSTDAPI_(BOOL)
  177. SHDeleteTimerQueue(
  178.     IN HANDLE hQueue
  179.     )
  180. /*++
  181. Routine Description:
  182.     Deletes the specified timer queue
  183. Arguments:
  184.     hQueue  - handle of queue to delete; NULL for default timer queue
  185. Return Value:
  186.     BOOL
  187.         Success - TRUE
  188.         Failure - FALSE. Call GetLastError() for more info
  189. --*/
  190. {
  191.     InterlockedIncrement((LPLONG)&g_ActiveRequests);
  192.     BOOL bSuccess = FALSE;
  193.     if (!g_bTpsTerminating) {
  194.         if (hQueue == NULL) {
  195.             hQueue = g_hDefaultTimerQueue;
  196.         }
  197.         if ((hQueue != NULL) && (g_hTimerThread != NULL)) {
  198.             CTimerQueueDeleteRequest request(hQueue);
  199.             if (QueueUserAPC((PAPCFUNC)DeleteTimerQueue,
  200.                              g_hTimerThread,
  201.                              (ULONG_PTR)&request)) {
  202.                 request.WaitForCompletion();
  203.                 bSuccess = request.SetThreadStatus();
  204.             } else {
  205. #if DBG
  206.                 DWORD error = GetLastError();
  207.                 ASSERT(error == ERROR_SUCCESS);
  208. #endif
  209.             }
  210.         } else {
  211.             SetLastError(ERROR_INVALID_PARAMETER); // BUGBUG - correct error code?
  212.         }
  213.     } else {
  214.         SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // BUGBUG - error code?
  215.     }
  216.     InterlockedDecrement((LPLONG)&g_ActiveRequests);
  217.     return bSuccess;
  218. }
  219. LWSTDAPI_(HANDLE)
  220. SHSetTimerQueueTimer(
  221.     IN HANDLE hQueue,
  222.     IN WAITORTIMERCALLBACKFUNC pfnCallback,
  223.     IN LPVOID pContext,
  224.     IN DWORD dwDueTime,
  225.     IN DWORD dwPeriod,
  226.     IN LPCSTR lpszLibrary OPTIONAL,
  227.     IN DWORD dwFlags
  228.     )
  229. /*++
  230. Routine Description:
  231.     Add a timer to a queue
  232. Arguments:
  233.     hQueue      - handle of timer queue; NULL for default queue
  234.     pfnCallback - function to call when timer triggers
  235.     pContext    - parameter to pfnCallback
  236.     dwDueTime   - initial firing time in milliseconds from now
  237.     dwPeriod    - repeating period. 0 for one-shot
  238.     lpszLibrary - if specified, name of library (DLL) to reference
  239.     dwFlags     - flags controlling function:
  240.                     TPS_EXECUTEIO   - Execute callback in I/O thread
  241. Return Value:
  242.     HANDLE
  243.         Success - non-NULL handle
  244.         Failure - NULL. Call GetLastError() for more info
  245. --*/
  246. {
  247.     InterlockedIncrement((LPLONG)&g_ActiveRequests);
  248.     HANDLE hTimer = NULL;
  249.     if (!g_bTpsTerminating) {
  250.         DWORD error = ERROR_SUCCESS;
  251.         if (g_hTimerThread == NULL) {
  252.             error = InitializeTimerThread();
  253.         }
  254.         ASSERT(g_hTimerThread != NULL);
  255.         if (error == ERROR_SUCCESS) {
  256.             if (hQueue == NULL) {
  257.                 hQueue = CreateDefaultTimerQueue();
  258.             }
  259.             if (hQueue != NULL) {
  260.                 CTimerAddRequest * pRequest = new CTimerAddRequest(hQueue,
  261.                                                                    pfnCallback,
  262.                                                                    pContext,
  263.                                                                    dwDueTime,
  264.                                                                    dwPeriod,
  265.                                                                    dwFlags
  266.                                                                    );
  267.                 if (pRequest != NULL) {
  268.                     hTimer = pRequest->GetHandle();
  269.                     if (QueueUserAPC((PAPCFUNC)AddTimer,
  270.                                      g_hTimerThread,
  271.                                      (ULONG_PTR)pRequest
  272.                                      )) {
  273.                     } else {
  274. #if DBG
  275.                         error = GetLastError();
  276.                         ASSERT(GetLastError() != ERROR_SUCCESS);
  277. #endif
  278.                         delete pRequest;
  279.                         hTimer = NULL;
  280. #if DBG
  281.                         SetLastError(error);
  282. #endif
  283.                     }
  284.                 }
  285.             }
  286.         } else {
  287.             SetLastError(error);
  288.         }
  289.     } else {
  290.         SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // BUGBUG - error code?
  291.     }
  292.     InterlockedDecrement((LPLONG)&g_ActiveRequests);
  293.     return hTimer;
  294. }
  295. LWSTDAPI_(BOOL)
  296. SHChangeTimerQueueTimer(
  297.     IN HANDLE hQueue,
  298.     IN HANDLE hTimer,
  299.     IN DWORD dwDueTime,
  300.     IN DWORD dwPeriod
  301.     )
  302. /*++
  303. Routine Description:
  304.     Change the due time or periodicity of a timer
  305. Arguments:
  306.     hQueue      - handle of queue on which timer resides. NULL for default queue
  307.     hTimer      - handle of timer to change
  308.     dwDueTime   - new due time
  309.     dwPeriod    - new period
  310. Return Value:
  311.     BOOL
  312.         Success - TRUE
  313.         Failure - FALSE. Call GetLastError() for more info
  314. --*/
  315. {
  316.     InterlockedIncrement((LPLONG)&g_ActiveRequests);
  317.     BOOL bSuccess = FALSE;
  318.     DWORD error = ERROR_SHUTDOWN_IN_PROGRESS; // BUGBUG - error code?
  319.     if (!g_bTpsTerminating) {
  320.         error = ERROR_OBJECT_NOT_FOUND;
  321.         if (g_hTimerThread != NULL) {
  322.             if (hQueue == NULL) {
  323.                 hQueue = g_hDefaultTimerQueue;
  324.             }
  325.             if (hQueue != NULL) {
  326.                 CTimerChangeRequest request(hQueue, hTimer, dwDueTime, dwPeriod);
  327.                 error = ERROR_SUCCESS; // both paths call SetLastError() if reqd
  328.                 if (QueueUserAPC((PAPCFUNC)ChangeTimer,
  329.                                  g_hTimerThread,
  330.                                  (ULONG_PTR)&request
  331.                                  )) {
  332.                     request.WaitForCompletion();
  333.                     bSuccess = request.SetThreadStatus();
  334.                 } else {
  335. #if DBG
  336.                     DWORD error = GetLastError();
  337.                     ASSERT(error == ERROR_SUCCESS);
  338. #endif
  339.                 }
  340.             }
  341.         }
  342.     }
  343.     InterlockedDecrement((LPLONG)&g_ActiveRequests);
  344.     if (error != ERROR_SUCCESS) {
  345.         SetLastError(error);
  346.     }
  347.     return bSuccess;
  348. }
  349. LWSTDAPI_(BOOL)
  350. SHCancelTimerQueueTimer(
  351.     IN HANDLE hQueue,
  352.     IN HANDLE hTimer
  353.     )
  354. /*++
  355. Routine Description:
  356.     Cancels a timer
  357. Arguments:
  358.     hQueue  - handle to queue on which timer resides
  359.     hTimer  - handle of timer to cancel
  360. Return Value:
  361.     BOOL
  362.         Success - TRUE
  363.         Failure - FALSE. Call GetLastError() for more info
  364. --*/
  365. {
  366.     InterlockedIncrement((LPLONG)&g_ActiveRequests);
  367.     BOOL bSuccess = FALSE;
  368.     if (!g_bTpsTerminating) {
  369.         if (hQueue == NULL) {
  370.             hQueue = g_hDefaultTimerQueue;
  371.         }
  372.         if ((hQueue != NULL) && (g_hTimerThread != NULL)) {
  373.             CTimerCancelRequest request(hQueue, hTimer);
  374.             if (QueueUserAPC((PAPCFUNC)CancelTimer,
  375.                              g_hTimerThread,
  376.                              (ULONG_PTR)&request
  377.                              )) {
  378.                 request.WaitForCompletion();
  379.                 bSuccess = request.SetThreadStatus();
  380.             } else {
  381. #if DBG
  382.                 DWORD error = GetLastError();
  383.                 ASSERT(error == ERROR_SUCCESS);
  384. #endif
  385.             }
  386.         } else {
  387.             SetLastError(ERROR_INVALID_HANDLE);
  388.         }
  389.     } else {
  390.         SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // BUGBUG - error code?
  391.     }
  392.     InterlockedDecrement((LPLONG)&g_ActiveRequests);
  393.     return bSuccess;
  394. }
  395. //
  396. // private functions
  397. //
  398. PRIVATE
  399. DWORD
  400. InitializeTimerThread(
  401.     VOID
  402.     )
  403. {
  404.     DWORD error = ERROR_SUCCESS;
  405.     while (!g_bTimerInitDone) {
  406.         if (!InterlockedExchange((LPLONG)&g_bTimerInit, TRUE)) {
  407.             //
  408.             // N.B. if CTimerQueueList::Init() does anything more than just
  409.             // initialize lists then add a Deinit()
  410.             //
  411.             g_TimerQueueList.Init();
  412.             ASSERT(g_hTimerThread == NULL);
  413.             error = StartThread((LPTHREAD_START_ROUTINE)TimerThread,
  414.                                 &g_hTimerThread,
  415.                                 FALSE
  416.                                 );
  417.             if (error == ERROR_SUCCESS) {
  418.                 g_bTimerInitDone = TRUE;
  419.             } else {
  420.                 InterlockedExchange((LPLONG)&g_bTimerInit, FALSE);
  421.             }
  422.             break;
  423.         } else {
  424.             SleepEx(0, FALSE);
  425.         }
  426.     }
  427.     return error;
  428. }
  429. PRIVATE
  430. VOID
  431. TimerCleanup(
  432.     VOID
  433.     )
  434. {
  435.     while (!g_TimerQueueList.QueueListHead()->IsEmpty()) {
  436.         CTimerQueueDeleteRequest request((CTimerQueue *)
  437.                                     g_TimerQueueList.QueueListHead()->Next());
  438.         DeleteTimerQueue(&request);
  439.     }
  440.     DeleteDefaultTimerQueue();
  441.     g_UID = 0;
  442.     g_bTimerInit = FALSE;
  443.     g_bTimerInitDone = FALSE;
  444. }
  445. BOOL bDefaultQueueInit = FALSE;
  446. BOOL bDefaultQueueInitDone = FALSE;
  447. BOOL bDefaultQueueInitFailed = FALSE;
  448. PRIVATE
  449. HANDLE
  450. CreateDefaultTimerQueue(
  451.     VOID
  452.     )
  453. {
  454.     do {
  455.         if ((g_hDefaultTimerQueue != NULL) || bDefaultQueueInitFailed) {
  456.             return g_hDefaultTimerQueue;
  457.         }
  458.         if (!InterlockedExchange((LPLONG)&bDefaultQueueInit, TRUE)) {
  459.             InterlockedExchange((LPLONG)&bDefaultQueueInitDone, FALSE);
  460.             g_hDefaultTimerQueue = SHCreateTimerQueue();
  461.             if (g_hDefaultTimerQueue == NULL) {
  462.                 bDefaultQueueInitFailed = TRUE;
  463.                 InterlockedExchange((LPLONG)&bDefaultQueueInit, FALSE);
  464.             }
  465.             InterlockedExchange((LPLONG)&bDefaultQueueInitDone, TRUE);
  466.         } else {
  467.             do {
  468.                 SleepEx(0, FALSE);
  469.             } while (!bDefaultQueueInitDone);
  470.         }
  471.     } while (TRUE);
  472. }
  473. PRIVATE
  474. VOID
  475. DeleteDefaultTimerQueue(
  476.     VOID
  477.     )
  478. {
  479.     if (g_hDefaultTimerQueue != NULL) {
  480.         CTimerQueueDeleteRequest request((CTimerQueue *)g_hDefaultTimerQueue);
  481.         DeleteTimerQueue(&request);
  482.         g_hDefaultTimerQueue = NULL;
  483.     }
  484.     CleanupDefaultTimerQueue();
  485. }
  486. PRIVATE
  487. VOID
  488. CleanupDefaultTimerQueue(
  489.     VOID
  490.     )
  491. {
  492.     g_hDefaultTimerQueue = NULL;
  493.     bDefaultQueueInit = FALSE;
  494.     bDefaultQueueInitDone = FALSE;
  495.     bDefaultQueueInitFailed = FALSE;
  496. }
  497. PRIVATE
  498. VOID
  499. TimerThread(
  500.     VOID
  501.     )
  502. {
  503.     g_dwTimerId = GetCurrentThreadId();
  504.     HMODULE hDll = LoadLibrary(g_cszShlwapi);
  505.     ASSERT(hDll != NULL);
  506.     ASSERT(g_TpsTls != 0xFFFFFFFF);
  507.     TlsSetValue(g_TpsTls, (LPVOID)TPS_TIMER_SIGNATURE);
  508.     while (!g_bTpsTerminating || (g_ActiveRequests != 0)) {
  509.         if (g_TimerQueueList.Wait()) {
  510.             if (g_bTpsTerminating && (g_ActiveRequests == 0)) {
  511.                 break;
  512.             }
  513.             g_TimerQueueList.ProcessCompletions();
  514.         }
  515.     }
  516.     ASSERT(g_hTimerThread != NULL);
  517.     CloseHandle(g_hTimerThread);
  518.     g_hTimerThread = NULL;
  519.     if (g_dwTimerId == g_dwTerminationThreadId) {
  520.         TimerCleanup();
  521.         g_bTpsTerminating = FALSE;
  522.         g_dwTerminationThreadId = 0;
  523.         g_bDeferredTimerTermination = FALSE;
  524.     }
  525.     g_dwTimerId = 0;
  526.     FreeLibraryAndExitThread(hDll, ERROR_SUCCESS);
  527. }
  528. PRIVATE
  529. VOID
  530. DeleteTimerQueue(
  531.     IN CTimerQueueDeleteRequest * pRequest
  532.     )
  533. {
  534.     CTimerQueue * pQueue = (CTimerQueue *)pRequest->GetQueue();
  535.     DWORD dwStatus = ERROR_INVALID_PARAMETER;
  536.     if (g_TimerQueueList.FindQueue((CDoubleLinkedListEntry *)pQueue) != NULL) {
  537.         pQueue->DeleteTimers();
  538.         if (pQueue == g_hDefaultTimerQueue) {
  539.             CleanupDefaultTimerQueue();
  540.         }
  541.         delete pQueue;
  542.         dwStatus = ERROR_SUCCESS;
  543.     }
  544.     pRequest->SetCompletionStatus(dwStatus);
  545. }
  546. PRIVATE
  547. VOID
  548. AddTimer(
  549.     IN CTimerAddRequest * pRequest
  550.     )
  551. {
  552.     CTimerQueue * pQueue = pRequest->GetQueue();
  553.     //
  554.     // add timer object to global list of timer objects, in expiration time
  555.     // order
  556.     //
  557.     pRequest->InsertBack(g_TimerQueueList.TimerListHead());
  558.     //
  559.     // add timer object to end of timer queue list in no particular order. Only
  560.     // used to delete all objects belonging to queue when queue is deleted
  561.     //
  562.     pRequest->TimerListHead()->InsertTail(pQueue->TimerListHead());
  563.     pRequest->SetComplete();
  564. }
  565. PRIVATE
  566. VOID
  567. ChangeTimer(
  568.     IN CTimerChangeRequest * pRequest
  569.     )
  570. {
  571.     CTimerQueue * pQueue = (CTimerQueue *)pRequest->GetQueue();
  572.     CTimerQueueEntry * pTimer = pQueue->FindTimer(pRequest->GetTimer());
  573.     DWORD dwStatus = ERROR_INVALID_PARAMETER;
  574.     if (pTimer != NULL) {
  575.         pTimer->SetPeriod(pRequest->GetPeriod());
  576.         pTimer->SetExpirationTime(pRequest->GetDueTime());
  577.         dwStatus = ERROR_SUCCESS;
  578.     }
  579.     pRequest->SetCompletionStatus(dwStatus);
  580. }
  581. PRIVATE
  582. VOID
  583. CancelTimer(
  584.     IN CTimerCancelRequest * pRequest
  585.     )
  586. {
  587.     CTimerQueue * pQueue = (CTimerQueue *)pRequest->GetQueue();
  588.     CTimerQueueEntry * pTimer = pQueue->FindTimer(pRequest->GetTimer());
  589.     DWORD dwStatus = ERROR_INVALID_PARAMETER;
  590.     if (pTimer != NULL) {
  591.         if (pTimer->IsInUse()) {
  592.             pTimer->SetCancelled();
  593.         } else {
  594.             pTimer->Remove();
  595.             delete pTimer;
  596.         }
  597.         dwStatus = ERROR_SUCCESS;
  598.     }
  599.     pRequest->SetCompletionStatus(dwStatus);
  600. }