migrate substitution keywords to SVN
[reactos.git] / reactos / lib / kernel32 / misc / timerqueue.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS system libraries
23 * PURPOSE: Timer Queue functions
24 * FILE: lib/kernel32/misc/timerqueue.c
25 * PROGRAMER: Thomas Weidenmueller <w3seek@reactos.com>
26 */
27
28 /* INCLUDES ******************************************************************/
29
30 #include <k32.h>
31
32 #define NDEBUG
33 #include "../include/debug.h"
34
35
36 /* FUNCTIONS *****************************************************************/
37
38 HANDLE DefaultTimerQueue = NULL;
39
40 /*
41 * Create the default timer queue for the current process. This function is only
42 * called if CreateTimerQueueTimer() or SetTimerQueueTimer() is called.
43 * However, ChangeTimerQueueTimer() fails with ERROR_INVALID_PARAMETER if the
44 * default timer queue has not been created, because it assumes there has to be
45 * a timer queue with a timer if it want's to be changed.
46 */
47 static BOOL
48 IntCreateDefaultTimerQueue(VOID)
49 {
50 NTSTATUS Status;
51
52 /* FIXME - make this thread safe */
53
54 /* create the timer queue */
55 Status = RtlCreateTimerQueue(&DefaultTimerQueue);
56 if(!NT_SUCCESS(Status))
57 {
58 SetLastErrorByStatus(Status);
59 DPRINT1("Unable to create the default timer queue!\n");
60 return FALSE;
61 }
62
63 return TRUE;
64 }
65
66 /*
67 * @implemented
68 */
69 BOOL
70 STDCALL
71 CancelTimerQueueTimer(HANDLE TimerQueue,
72 HANDLE Timer)
73 {
74 /* Since this function is not documented in PSDK and apparently does nothing
75 but delete the timer, we just do the same as DeleteTimerQueueTimer(), without
76 passing a completion event. */
77 NTSTATUS Status;
78
79 if(TimerQueue == NULL)
80 {
81 /* let's use the process' default timer queue. We assume the default timer
82 queue has been created with a previous call to CreateTimerQueueTimer() or
83 SetTimerQueueTimer(), otherwise this call wouldn't make much sense. */
84 if(!(TimerQueue = DefaultTimerQueue))
85 {
86 SetLastError(ERROR_INVALID_HANDLE);
87 return FALSE;
88 }
89 }
90
91 if(Timer == NULL)
92 {
93 SetLastError(ERROR_INVALID_PARAMETER);
94 return FALSE;
95 }
96
97 /* delete the timer */
98 Status = RtlDeleteTimer(TimerQueue, Timer, NULL);
99
100 if(!NT_SUCCESS(Status))
101 {
102 SetLastErrorByStatus(Status);
103 return FALSE;
104 }
105
106 return TRUE;
107 }
108
109 /*
110 * @implemented
111 */
112 BOOL
113 STDCALL
114 ChangeTimerQueueTimer(HANDLE TimerQueue,
115 HANDLE Timer,
116 ULONG DueTime,
117 ULONG Period)
118 {
119 NTSTATUS Status;
120
121 if(TimerQueue == NULL)
122 {
123 /* let's use the process' default timer queue. We assume the default timer
124 queue has been created with a previous call to CreateTimerQueueTimer() or
125 SetTimerQueueTimer(), otherwise this call wouldn't make much sense. */
126 if(!(TimerQueue = DefaultTimerQueue))
127 {
128 SetLastError(ERROR_INVALID_HANDLE);
129 return FALSE;
130 }
131 }
132
133 if(Timer == NULL)
134 {
135 SetLastError(ERROR_INVALID_PARAMETER);
136 return FALSE;
137 }
138
139 /* update the timer */
140 Status = RtlUpdateTimer(TimerQueue, Timer, DueTime, Period);
141 if(!NT_SUCCESS(Status))
142 {
143 SetLastErrorByStatus(Status);
144 return FALSE;
145 }
146
147 return TRUE;
148 }
149
150 /*
151 * @implemented
152 */
153 HANDLE
154 STDCALL
155 CreateTimerQueue(VOID)
156 {
157 HANDLE Handle;
158 NTSTATUS Status;
159
160 /* create the timer queue */
161 Status = RtlCreateTimerQueue(&Handle);
162 if(!NT_SUCCESS(Status))
163 {
164 SetLastErrorByStatus(Status);
165 return NULL;
166 }
167
168 return Handle;
169 }
170
171 /*
172 * @implemented
173 */
174 BOOL
175 STDCALL
176 CreateTimerQueueTimer(PHANDLE phNewTimer,
177 HANDLE TimerQueue,
178 WAITORTIMERCALLBACK Callback,
179 PVOID Parameter,
180 DWORD DueTime,
181 DWORD Period,
182 ULONG Flags)
183 {
184 NTSTATUS Status;
185
186 /* windows seems not to test this parameter at all, so we'll try to clear it here
187 so we don't crash somewhere inside ntdll */
188 *phNewTimer = NULL;
189
190 if(TimerQueue == NULL)
191 {
192 /* the default timer queue is requested, try to create it if it hasn't been already */
193 if(!(TimerQueue = DefaultTimerQueue))
194 {
195 if(!IntCreateDefaultTimerQueue())
196 {
197 /* IntCreateDefaultTimerQueue() set the last error code already, just fail */
198 return FALSE;
199 }
200 TimerQueue = DefaultTimerQueue;
201 }
202 }
203
204 /* !!! Win doesn't even check if Callback == NULL, so we don't, too! That'll
205 raise a nice exception later... */
206
207 /* create the timer */
208 Status = RtlCreateTimer(TimerQueue, phNewTimer, Callback, Parameter, DueTime,
209 Period, Flags);
210 if(!NT_SUCCESS(Status))
211 {
212 SetLastErrorByStatus(Status);
213 return FALSE;
214 }
215
216 return TRUE;
217 }
218
219 /*
220 * @implemented
221 */
222 BOOL
223 STDCALL
224 DeleteTimerQueue(HANDLE TimerQueue)
225 {
226 NTSTATUS Status;
227
228 /* We don't allow the user to delete the default timer queue */
229 if(TimerQueue == NULL)
230 {
231 SetLastError(ERROR_INVALID_HANDLE);
232 return FALSE;
233 }
234
235 /* delete the timer queue */
236 Status = RtlDeleteTimerQueue(TimerQueue);
237 return NT_SUCCESS(Status);
238 }
239
240 /*
241 * @implemented
242 */
243 BOOL
244 STDCALL
245 DeleteTimerQueueEx(HANDLE TimerQueue,
246 HANDLE CompletionEvent)
247 {
248 NTSTATUS Status;
249
250 /* We don't allow the user to delete the default timer queue */
251 if(TimerQueue == NULL)
252 {
253 SetLastError(ERROR_INVALID_HANDLE);
254 return FALSE;
255 }
256
257 /* delete the queue */
258 Status = RtlDeleteTimerQueueEx(TimerQueue, CompletionEvent);
259
260 if((CompletionEvent != INVALID_HANDLE_VALUE && Status == STATUS_PENDING) ||
261 !NT_SUCCESS(Status))
262 {
263 /* In case CompletionEvent == NULL, RtlDeleteTimerQueueEx() returns before
264 all callback routines returned. We set the last error code to STATUS_PENDING
265 and return FALSE. In case CompletionEvent == INVALID_HANDLE_VALUE we only
266 can get here if another error occured. In case CompletionEvent is something
267 else, we get here and fail, even though it isn't really an error (if Status == STATUS_PENDING).
268 We also handle all other failures the same way. */
269
270 SetLastErrorByStatus(Status);
271 return FALSE;
272 }
273
274 return TRUE;
275 }
276
277 /*
278 * @implemented
279 */
280 BOOL
281 STDCALL
282 DeleteTimerQueueTimer(HANDLE TimerQueue,
283 HANDLE Timer,
284 HANDLE CompletionEvent)
285 {
286 NTSTATUS Status;
287
288 if(TimerQueue == NULL)
289 {
290 /* let's use the process' default timer queue. We assume the default timer
291 queue has been created with a previous call to CreateTimerQueueTimer() or
292 SetTimerQueueTimer(), otherwise this call wouldn't make much sense. */
293 if(!(TimerQueue = DefaultTimerQueue))
294 {
295 SetLastError(ERROR_INVALID_HANDLE);
296 return FALSE;
297 }
298 }
299
300 if(Timer == NULL)
301 {
302 SetLastError(ERROR_INVALID_PARAMETER);
303 return FALSE;
304 }
305
306 /* delete the timer */
307 Status = RtlDeleteTimer(TimerQueue, Timer, CompletionEvent);
308
309 if((CompletionEvent != INVALID_HANDLE_VALUE && Status == STATUS_PENDING) ||
310 !NT_SUCCESS(Status))
311 {
312 /* In case CompletionEvent == NULL, RtlDeleteTimer() returns before
313 the callback routine returned. We set the last error code to STATUS_PENDING
314 and return FALSE. In case CompletionEvent == INVALID_HANDLE_VALUE we only
315 can get here if another error occured. In case CompletionEvent is something
316 else, we get here and fail, even though it isn't really an error (if Status == STATUS_PENDING).
317 We also handle all other failures the same way. */
318
319 SetLastErrorByStatus(Status);
320 return FALSE;
321 }
322
323 return TRUE;
324 }
325
326 /*
327 * @implemented
328 */
329 HANDLE
330 STDCALL
331 SetTimerQueueTimer(HANDLE TimerQueue,
332 WAITORTIMERCALLBACK Callback,
333 PVOID Parameter,
334 DWORD DueTime,
335 DWORD Period,
336 BOOL PreferIo)
337 {
338 /* Since this function is not documented in PSDK and apparently does nothing
339 but create a timer, we just do the same as CreateTimerQueueTimer(). Unfortunately
340 I don't really know what PreferIo is supposed to be, it propably just affects the
341 Flags parameter of CreateTimerQueueTimer(). Looking at the PSDK documentation of
342 CreateTimerQueueTimer() there's only one flag (WT_EXECUTEINIOTHREAD) that causes
343 the callback function queued to an I/O worker thread. I guess it uses this flag
344 if PreferIo == TRUE, otherwise let's just use WT_EXECUTEDEFAULT. We should
345 test this though, this is only guess work and I'm too lazy to do further
346 investigation. */
347
348 HANDLE Timer;
349 NTSTATUS Status;
350
351 if(TimerQueue == NULL)
352 {
353 /* the default timer queue is requested, try to create it if it hasn't been already */
354 if(!(TimerQueue = DefaultTimerQueue))
355 {
356 if(!IntCreateDefaultTimerQueue())
357 {
358 /* IntCreateDefaultTimerQueue() set the last error code already, just fail */
359 return FALSE;
360 }
361 TimerQueue = DefaultTimerQueue;
362 }
363 }
364
365 /* create the timer */
366 Status = RtlCreateTimer(TimerQueue, &Timer, Callback, Parameter, DueTime,
367 Period, (PreferIo ? WT_EXECUTEINIOTHREAD : WT_EXECUTEDEFAULT));
368 if(!NT_SUCCESS(Status))
369 {
370 SetLastErrorByStatus(Status);
371 return NULL;
372 }
373
374 return Timer;
375 }
376
377 /* EOF */