Source code of Windows XP (NT5)
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.

322 lines
8.6 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. This module implements POSIX timer related services.
  7. Author:
  8. Mark Lucovsky (markl) 08-Aug-1989
  9. Revision History:
  10. --*/
  11. #include "psxsrv.h"
  12. typedef struct _ALARM_WORK_ITEM {
  13. LIST_ENTRY Links;
  14. PPSX_PROCESS Process;
  15. LARGE_INTEGER Time;
  16. } ALARM_WORK_ITEM, *PALARM_WORK_ITEM;
  17. static LIST_ENTRY AlarmWorkList;
  18. static RTL_CRITICAL_SECTION AlarmWorkListMutex;
  19. HANDLE AlarmThreadHandle;
  20. HANDLE AlarmInitEvent;
  21. VOID
  22. AlarmApcRoutine(
  23. IN PVOID TimerContext,
  24. IN ULONG TimerLowValue,
  25. IN LONG TimerHighValue
  26. )
  27. /*++
  28. Routine Description:
  29. This function is called when a process alarm timer expires. Its purpose
  30. is to send a SIGALRM signal to the appropriate process.
  31. Arguments:
  32. TimerContext - Specifies the process that is to be signaled.
  33. TimerLowValue - Ignored.
  34. TimerHighValue - Ignored.
  35. Return Value:
  36. None.
  37. --*/
  38. {
  39. PPSX_PROCESS p;
  40. p = (PPSX_PROCESS)TimerContext;
  41. //
  42. // Get process table lock to see if process still has an alarm timer. If
  43. // it does, then signal the process. Otherwise, drop alarm on the floor.
  44. //
  45. AcquireProcessStructureLock();
  46. if (p->AlarmTimer) {
  47. PsxSignalProcess(p, SIGALRM);
  48. }
  49. ReleaseProcessStructureLock();
  50. }
  51. BOOLEAN
  52. PsxAlarm(
  53. IN PPSX_PROCESS p,
  54. IN PPSX_API_MSG m
  55. )
  56. /*++
  57. Routine Description:
  58. This function implements the alarm() API. The problem we're having
  59. here is that in order for the APC to be processed, the thread (who
  60. calls NtSetTimer) must be waiting in an alertable state. This is not
  61. the case for the Api Request Threads.
  62. So we have a thread dedicated to processing alarm requests. When he's
  63. not doing that, he waits on his own thread handle. The Api Request
  64. Thread puts a work request on a queue and wakes the alarm thread. He
  65. processes the work request and then waits again.
  66. Arguments:
  67. p - Supplies the address of the calling process
  68. m - Supplies the address of the related message
  69. Return Value:
  70. TRUE - Always succeeds and generates a reply
  71. --*/
  72. {
  73. PPSX_ALARM_MSG args;
  74. TIMER_BASIC_INFORMATION TimerInfo;
  75. NTSTATUS st;
  76. PALARM_WORK_ITEM pItem;
  77. HANDLE Timer;
  78. args = &m->u.Alarm;
  79. args->PreviousSeconds.LowPart = 0;
  80. args->PreviousSeconds.HighPart = 0;
  81. if (args->CancelAlarm) {
  82. // Cancel the timer.
  83. AcquireProcessStructureLock();
  84. Timer = p->AlarmTimer;
  85. p->AlarmTimer = NULL;
  86. //
  87. // After this point no alarms will be delivered to the process.
  88. //
  89. ReleaseProcessStructureLock();
  90. if (NULL != Timer) {
  91. //
  92. // Query timer to determine signaled state and time
  93. // remaining. If timer is already signaled, then
  94. // return 0; otherwise, return the reported remaining
  95. // time
  96. //
  97. st = NtQueryTimer(Timer, TimerBasicInformation,
  98. &TimerInfo, sizeof(TIMER_BASIC_INFORMATION),
  99. NULL);
  100. if (!NT_SUCCESS(st)) {
  101. KdPrint(("PSXSS: QueryTimer: 0x%x\n", st));
  102. m->Error = ENOMEM;
  103. return TRUE;
  104. }
  105. if (FALSE == TimerInfo.TimerState) {
  106. //
  107. // Timer is still active
  108. //
  109. args->PreviousSeconds.LowPart =
  110. TimerInfo.RemainingTime.LowPart;
  111. args->PreviousSeconds.HighPart =
  112. TimerInfo.RemainingTime.HighPart;
  113. st = NtCancelTimer(Timer,
  114. &TimerInfo.TimerState);
  115. ASSERT(NT_SUCCESS(st));
  116. ASSERT(FALSE == TimerInfo.TimerState);
  117. }
  118. st = NtClose(Timer);
  119. ASSERT(NT_SUCCESS(st));
  120. } else {
  121. //
  122. // The timer was already NULL, so we were cancelling
  123. // a timer that had not been set.
  124. //
  125. }
  126. return TRUE;
  127. }
  128. //
  129. // Set a timer.
  130. //
  131. if (p->AlarmTimer) {
  132. //
  133. // Query timer to determine signaled state and time remaining
  134. // If timer is already signaled, then return 0; otherwise,
  135. // return the reported remaining time.
  136. //
  137. st = NtQueryTimer(p->AlarmTimer, TimerBasicInformation,
  138. &TimerInfo, sizeof(TIMER_BASIC_INFORMATION), NULL);
  139. ASSERT(NT_SUCCESS(st));
  140. if (TimerInfo.TimerState == FALSE) {
  141. //
  142. // Timer is still active
  143. //
  144. args->PreviousSeconds.LowPart =
  145. TimerInfo.RemainingTime.LowPart;
  146. args->PreviousSeconds.HighPart =
  147. TimerInfo.RemainingTime.HighPart;
  148. st = NtCancelTimer(p->AlarmTimer,
  149. &TimerInfo.TimerState);
  150. ASSERT(NT_SUCCESS(st));
  151. }
  152. } else {
  153. //
  154. // Process does not have a timer, so create one for it.
  155. // The timer will not be deallocated until the process exits.
  156. //
  157. st = NtCreateTimer(&p->AlarmTimer,
  158. TIMER_ALL_ACCESS,
  159. NULL,
  160. NotificationTimer);
  161. if (!NT_SUCCESS(st)) {
  162. m->Error = ENOMEM;
  163. return TRUE;
  164. }
  165. }
  166. //
  167. // Arrange for the alarm thread to set the timer.
  168. //
  169. st = NtResetEvent(AlarmInitEvent, NULL);
  170. ASSERT(NT_SUCCESS(st));
  171. pItem = (PVOID)RtlAllocateHeap(PsxHeap, 0, sizeof(*pItem));
  172. if (NULL == pItem) {
  173. m->Error = ENOMEM;
  174. return TRUE;
  175. }
  176. pItem->Process = p;
  177. pItem->Time = args->Seconds;
  178. RtlEnterCriticalSection(&AlarmWorkListMutex);
  179. InsertTailList(&AlarmWorkList, &pItem->Links);
  180. RtlLeaveCriticalSection(&AlarmWorkListMutex);
  181. //
  182. // Wake up the Alarm Thread to process the work item.
  183. //
  184. st = NtAlertThread(AlarmThreadHandle);
  185. ASSERT(NT_SUCCESS(st));
  186. //
  187. // Block until the work item has been processed. If we don't do
  188. // this, there can be cases where an alarm is queried before the
  189. // alarm thread has actually initialized the timer.
  190. //
  191. st = NtWaitForSingleObject(AlarmInitEvent, FALSE, NULL);
  192. return TRUE;
  193. }
  194. VOID
  195. AlarmThreadRoutine(VOID)
  196. {
  197. NTSTATUS Status;
  198. PALARM_WORK_ITEM pItem;
  199. RtlInitializeCriticalSection(&AlarmWorkListMutex);
  200. InitializeListHead(&AlarmWorkList);
  201. Status = NtCreateEvent(&AlarmInitEvent, EVENT_ALL_ACCESS,
  202. NULL, NotificationEvent, TRUE);
  203. ASSERT(NT_SUCCESS(Status));
  204. for (;;) {
  205. (void)NtWaitForSingleObject(NtCurrentThread(), TRUE, NULL);
  206. RtlEnterCriticalSection(&AlarmWorkListMutex);
  207. while (!IsListEmpty(&AlarmWorkList)) {
  208. pItem = (PVOID)RemoveHeadList(&AlarmWorkList);
  209. Status = NtSetTimer(pItem->Process->AlarmTimer,
  210. &pItem->Time,
  211. AlarmApcRoutine,
  212. pItem->Process,
  213. FALSE,
  214. 0,
  215. NULL);
  216. if (!NT_SUCCESS(Status)) {
  217. KdPrint(("PSXSS: AlarmThread: NtSetTime: 0x%x\n", Status));
  218. }
  219. RtlFreeHeap(PsxHeap, 0, pItem);
  220. }
  221. RtlLeaveCriticalSection(&AlarmWorkListMutex);
  222. Status = NtSetEvent(AlarmInitEvent, NULL);
  223. ASSERT(NT_SUCCESS(Status));
  224. }
  225. }