Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
9.5 KiB

  1. //
  2. // idletimr.c
  3. //
  4. // This file contains an internal handling process for monitoring
  5. // idled "wait for text" threads, and dispatching callback messages
  6. // to the front-end application regarding the current state. This is
  7. // very helpful when debugging scripts, by seeing exactly what
  8. // the script is waiting on.
  9. //
  10. // Copyright (C) 2001 Microsoft Corporation
  11. //
  12. // Author: a-devjen (Devin Jenson)
  13. //
  14. #include <limits.h>
  15. #include <stdlib.h>
  16. #include "idletimr.h"
  17. #include "connlist.h"
  18. #include "apihandl.h"
  19. // These are the internal messages used for the thread message queue
  20. // to indicate what action to take.
  21. #define WM_SETTIMER WM_USER + 1
  22. #define WM_KILLTIMER WM_USER + 2
  23. // Internal helper function prototypes
  24. DWORD WINAPI T2WaitTimerThread(LPVOID lpParameter);
  25. void CALLBACK T2WaitTimerProc(HWND Window,
  26. UINT Message, UINT_PTR TimerId, DWORD TimePassed);
  27. // These are the callback routines
  28. PFNPRINTMESSAGE pfnPrintMessage = NULL;
  29. PFNIDLEMESSAGE pfnIdleCallback = NULL;
  30. // Timer and thread data, only one queue thread is needed
  31. // for all handles in the current process.
  32. HANDLE ThreadHandle = NULL;
  33. DWORD ThreadId = 0;
  34. BOOL ThreadIsOn = FALSE;
  35. BOOL ThreadIsStopping = FALSE;
  36. // CreateTimerThread
  37. //
  38. // Initializes the thread message queue required for monitoring timers.
  39. //
  40. // Returns true if the thread was successfully created, FALSE otherwise.
  41. BOOL T2CreateTimerThread(PFNPRINTMESSAGE PrintMessage,
  42. PFNIDLEMESSAGE IdleCallback)
  43. {
  44. // Return TRUE if the thread is already created...
  45. if (ThreadIsOn == TRUE)
  46. return TRUE;
  47. // Indicate the thread is on (for multithreaded apps)
  48. ThreadIsOn = TRUE;
  49. // Record the callback functions
  50. pfnPrintMessage = PrintMessage;
  51. pfnIdleCallback = IdleCallback;
  52. // Initialize thread creation
  53. ThreadHandle = CreateThread(NULL, 0,
  54. T2WaitTimerThread, NULL, 0, &ThreadId);
  55. // Check if we did OK
  56. if (ThreadHandle == NULL)
  57. {
  58. // We failed, reset all the global variables
  59. ThreadHandle = NULL;
  60. ThreadId = 0;
  61. pfnPrintMessage = NULL;
  62. pfnIdleCallback = NULL;
  63. // Turn the thread off and return failure
  64. ThreadIsOn = FALSE;
  65. return FALSE;
  66. }
  67. return TRUE;
  68. }
  69. // DestroyTimerThread
  70. //
  71. // Destroys the thread created by CreateTimerThread.
  72. //
  73. // Returns TRUE on success, FALSE on failure.
  74. BOOL T2DestroyTimerThread(void)
  75. {
  76. // Record the current thread handle locally because
  77. // the global value could possibly be changed.
  78. HANDLE LocalThreadHandle = ThreadHandle;
  79. // If the thread is already stopped, return success
  80. if (ThreadIsOn == FALSE)
  81. return TRUE;
  82. // If the thread is already trying to stop, return failure
  83. if (ThreadIsStopping == TRUE)
  84. return FALSE;
  85. // Indicate the thread is now trying to stop
  86. ThreadIsStopping = TRUE;
  87. // First send the thread a quit message
  88. PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
  89. // Wait for the thread (5 seconds)
  90. if (WaitForSingleObject(LocalThreadHandle, 5000) == WAIT_TIMEOUT) {
  91. // Why is our idle timer waiting??
  92. _ASSERT(FALSE);
  93. // No more waiting, we now use brute force
  94. TerminateThread(LocalThreadHandle, -1);
  95. }
  96. // Close the thread handle
  97. CloseHandle(LocalThreadHandle);
  98. // Clear out all the global variables
  99. ThreadHandle = NULL;
  100. ThreadId = 0;
  101. pfnPrintMessage = NULL;
  102. pfnIdleCallback = NULL;
  103. // And of course, release the thread switches
  104. ThreadIsOn = FALSE;
  105. ThreadIsStopping = FALSE;
  106. return TRUE;
  107. }
  108. // StartTimer
  109. //
  110. // Call this before going into a wait state for the thread on
  111. // "wait for text". This will record the current time, and start
  112. // a timer which will execute in exactly WAIT_TIME milliseconds.
  113. // When this occurs, the callback routines are notified.
  114. //
  115. // No return value.
  116. void T2StartTimer(HANDLE Connection, LPCWSTR Label)
  117. {
  118. TSAPIHANDLE *Handle = (TSAPIHANDLE *)Connection;
  119. // Make sure the message queue is on first
  120. if (ThreadIsOn == FALSE || ThreadIsStopping == TRUE)
  121. return;
  122. // Record the label for this timer
  123. if (Label == NULL)
  124. *(Handle->WaitStr) = '\0';
  125. else
  126. wcstombs(Handle->WaitStr, Label, MAX_PATH);
  127. // Post a message to the thread regarding a new timer for the handle.
  128. PostThreadMessage(ThreadId, WM_SETTIMER, WAIT_TIME, (LPARAM)Connection);
  129. }
  130. // StopTimer
  131. //
  132. // Call this after text is received in a "wait for text" thread state.
  133. // It will stop the timer created by StartTimer preventing further
  134. // messages with the recorded label.
  135. //
  136. // No return value.
  137. void T2StopTimer(HANDLE Connection)
  138. {
  139. // Make sure the message queue is on first
  140. if (ThreadIsOn == FALSE || ThreadIsStopping == TRUE)
  141. return;
  142. // Post a message to the thread to tell it to stop the timer
  143. PostThreadMessage(ThreadId, WM_KILLTIMER, 0, (LPARAM)Connection);
  144. }
  145. // WaitTimerProc
  146. //
  147. // When a timer exceeds it maximum time, this function is called.
  148. // This will stop the timer, and start it back up for an additionaly
  149. // interval of WAIT_TIME_STEP. This is after it sends the notifications
  150. // back to the user callback functions.
  151. //
  152. // This function is a valid format for use as a callback function in
  153. // use with the SetTimer Win32 API function.
  154. //
  155. // No return value.
  156. /* This recieves notifications when a timer actually elapses */
  157. void CALLBACK T2WaitTimerProc(HWND Window, UINT Message,
  158. UINT_PTR TimerId, DWORD TickCount)
  159. {
  160. DWORD IdleSecs = 0;
  161. DWORD TimeStarted = 0;
  162. // First get the the handle for the specified timer id
  163. HANDLE Connection = T2ConnList_FindHandleByTimerId(TimerId);
  164. // Stop the current running timer
  165. KillTimer(NULL, TimerId);
  166. // Do a sanity check to make sure we have a handle for this timer
  167. if (Connection == NULL) {
  168. _ASSERT(FALSE);
  169. return;
  170. }
  171. // Clear the time id parameter in the linked list
  172. T2ConnList_SetTimerId(Connection, 0);
  173. // Get the time this timer began
  174. T2ConnList_GetData(Connection, NULL, &TimeStarted);
  175. // Calculate number of seconds this timer has been running
  176. // (from its millisecond value)
  177. IdleSecs = (TickCount - TimeStarted) / 1000;
  178. // Call the PrintMessage callback function first
  179. if (pfnPrintMessage != NULL)
  180. pfnPrintMessage(IDLE_MESSAGE, "(Idle %u Secs) %s [%X]\n",
  181. IdleSecs, ((TSAPIHANDLE *)Connection)->WaitStr, Connection);
  182. // Secondly call the IdleCallback callback function
  183. if (pfnIdleCallback != NULL)
  184. pfnIdleCallback(Connection,
  185. ((TSAPIHANDLE *)Connection)->WaitStr, IdleSecs);
  186. // Reestablish a new timer with WAIT_TIME STEP to do this process again
  187. TimerId = SetTimer(NULL, 0, WAIT_TIME_STEP, T2WaitTimerProc);
  188. // Record the new timer id
  189. T2ConnList_SetTimerId(Connection, TimerId);
  190. }
  191. // WaitTimerThread
  192. //
  193. // This is a valid thread message queue. It is created using the
  194. // CreateTimerThread function, and killed using the DestroyTimerThread
  195. // function. It is more-or-less a worker thread to create SetTimer
  196. // callback functions which cannot be used in the main thread because
  197. // if the thread goes into wait state, SetTimer callbacks will not be
  198. // called. When you need to add/remove a thread, use the following
  199. // thread posting form:
  200. //
  201. // UINT Message = WM_SETTIMER or WM_KILLTIMER
  202. // WPARAM wParam = Initial wait time (usually WAIT_TIME)
  203. // LPARAM lParam = (HANDLE)Connection
  204. //
  205. // The return value is always 0.
  206. DWORD WINAPI T2WaitTimerThread(LPVOID lpParameter)
  207. {
  208. UINT_PTR TimerId;
  209. MSG Message;
  210. UINT WaitTime;
  211. // This is the message queue function for the thread
  212. while(GetMessage(&Message, NULL, 0, 0) > 0)
  213. {
  214. TimerId = 0;
  215. // SetTimer uses a UINT timeout value, while a WPARAM is a
  216. // pointer-sized value.
  217. WaitTime = Message.wParam > UINT_MAX ? UINT_MAX :
  218. (UINT)Message.wParam;
  219. // Enumerate the retreived message
  220. switch(Message.message)
  221. {
  222. // Create a new timer for a specified handle
  223. case WM_SETTIMER:
  224. // Create the timer and record its new timer id
  225. TimerId = SetTimer(NULL, 0, WaitTime, T2WaitTimerProc);
  226. T2ConnList_SetData((HANDLE)(Message.lParam),
  227. TimerId, GetTickCount());
  228. break;
  229. // Stop a running timer for the specified handle
  230. case WM_KILLTIMER:
  231. // Get the timer id for the handle
  232. T2ConnList_GetData((HANDLE)(Message.lParam), &TimerId, NULL);
  233. // Validate and clear the timer if valid
  234. if (TimerId != 0 && TimerId != -1)
  235. KillTimer(NULL, TimerId);
  236. // Clear the linked last data for the handle
  237. T2ConnList_SetData((HANDLE)Message.lParam, 0, 0);
  238. break;
  239. // Indicates a timer has elapsed its time, call the
  240. // procedure that handles these messages.
  241. case WM_TIMER:
  242. T2WaitTimerProc(NULL, WM_TIMER, WaitTime, GetTickCount());
  243. break;
  244. }
  245. }
  246. // Clear out the thread values
  247. ThreadHandle = NULL;
  248. ThreadId = 0;
  249. return 0;
  250. }