reshuffling of dlls
[reactos.git] / reactos / dll / win32 / kernel32 / thread / thread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/kernel32/thread/thread.c
5 * PURPOSE: Thread functions
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Ariadne ( ariadne@xs4all.nl)
8 *
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <k32.h>
14
15 #define NDEBUG
16 #include "../include/debug.h"
17
18 /* FIXME: NDK */
19 #define HIGH_PRIORITY 31
20
21 /* FUNCTIONS *****************************************************************/
22 _SEH_FILTER(BaseThreadExceptionFilter)
23 {
24 EXCEPTION_POINTERS * ExceptionInfo = _SEH_GetExceptionPointers();
25 LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
26
27 if (GlobalTopLevelExceptionFilter != NULL)
28 {
29 _SEH_TRY
30 {
31 ExceptionDisposition = GlobalTopLevelExceptionFilter(ExceptionInfo);
32 }
33 _SEH_HANDLE
34 {
35 ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
36 }
37 _SEH_END;
38 }
39
40 return ExceptionDisposition;
41 }
42
43 __declspec(noreturn)
44 VOID
45 STDCALL
46 BaseThreadStartup(LPTHREAD_START_ROUTINE lpStartAddress,
47 LPVOID lpParameter)
48 {
49 volatile UINT uExitCode = 0;
50
51 /* Attempt to call the Thread Start Address */
52 _SEH_TRY
53 {
54 /* Get the exit code from the Thread Start */
55 uExitCode = (lpStartAddress)((PVOID)lpParameter);
56 }
57 _SEH_EXCEPT(BaseThreadExceptionFilter)
58 {
59 /* Get the Exit code from the SEH Handler */
60 uExitCode = _SEH_GetExceptionCode();
61 } _SEH_END;
62
63 /* Exit the Thread */
64 ExitThread(uExitCode);
65 }
66
67 /*
68 * @implemented
69 */
70 HANDLE
71 STDCALL
72 CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
73 DWORD dwStackSize,
74 LPTHREAD_START_ROUTINE lpStartAddress,
75 LPVOID lpParameter,
76 DWORD dwCreationFlags,
77 LPDWORD lpThreadId)
78 {
79 /* Act as if we're going to create a remote thread in ourselves */
80 return CreateRemoteThread(NtCurrentProcess(),
81 lpThreadAttributes,
82 dwStackSize,
83 lpStartAddress,
84 lpParameter,
85 dwCreationFlags,
86 lpThreadId);
87 }
88
89 /*
90 * @implemented
91 */
92 HANDLE
93 STDCALL
94 CreateRemoteThread(HANDLE hProcess,
95 LPSECURITY_ATTRIBUTES lpThreadAttributes,
96 DWORD dwStackSize,
97 LPTHREAD_START_ROUTINE lpStartAddress,
98 LPVOID lpParameter,
99 DWORD dwCreationFlags,
100 LPDWORD lpThreadId)
101 {
102 NTSTATUS Status;
103 INITIAL_TEB InitialTeb;
104 CONTEXT Context;
105 CLIENT_ID ClientId;
106 OBJECT_ATTRIBUTES LocalObjectAttributes;
107 POBJECT_ATTRIBUTES ObjectAttributes;
108 HANDLE hThread;
109 ULONG Dummy;
110
111 DPRINT("CreateRemoteThread: hProcess: %ld dwStackSize: %ld lpStartAddress"
112 ": %p lpParameter: %lx, dwCreationFlags: %lx\n", hProcess,
113 dwStackSize, lpStartAddress, lpParameter, dwCreationFlags);
114
115 /* Clear the Context */
116 RtlZeroMemory(&Context, sizeof(CONTEXT));
117
118 /* Write PID */
119 ClientId.UniqueProcess = hProcess;
120
121 /* Create the Stack */
122 Status = BasepCreateStack(hProcess,
123 dwStackSize,
124 dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION ?
125 dwStackSize : 0,
126 &InitialTeb);
127 if(!NT_SUCCESS(Status))
128 {
129 SetLastErrorByStatus(Status);
130 return NULL;
131 }
132
133 /* Create Initial Context */
134 BasepInitializeContext(&Context,
135 lpParameter,
136 lpStartAddress,
137 InitialTeb.StackBase,
138 1);
139
140 /* initialize the attributes for the thread object */
141 ObjectAttributes = BasepConvertObjectAttributes(&LocalObjectAttributes,
142 lpThreadAttributes,
143 NULL);
144
145 /* Create the Kernel Thread Object */
146 Status = NtCreateThread(&hThread,
147 THREAD_ALL_ACCESS,
148 ObjectAttributes,
149 hProcess,
150 &ClientId,
151 &Context,
152 &InitialTeb,
153 TRUE);
154 if(!NT_SUCCESS(Status))
155 {
156 BasepFreeStack(hProcess, &InitialTeb);
157 SetLastErrorByStatus(Status);
158 return NULL;
159 }
160
161 #ifdef SXS_SUPPORT_ENABLED
162 /* Are we in the same process? */
163 if (Process = NtCurrentProcess())
164 {
165 PTEB Teb;
166 PVOID ActivationContextStack;
167 PTHREAD_BASIC_INFORMATION ThreadBasicInfo;
168 PACTIVATION_CONTEXT_BASIC_INFORMATION ActivationCtxInfo;
169 ULONG_PTR Cookie;
170
171 /* Get the TEB */
172 Status = NtQueryInformationThread(hThread,
173 ThreadBasicIformation,
174 &ThreadBasicInfo,
175 sizeof(ThreadBasicInfo),
176 NULL);
177
178 /* Allocate the Activation Context Stack */
179 Status = RtlAllocateActivationContextStack(&ActivationContextStack);
180 Teb = ThreadBasicInfo.TebBaseAddress;
181
182 /* Save it */
183 Teb->ActivationContextStackPointer = ActivationContextStack;
184
185 /* Query the Context */
186 Status = RtlQueryInformationActivationContext(1,
187 0,
188 NULL,
189 ActivationContextBasicInformation,
190 &ActivationCtxInfo,
191 sizeof(ActivationCtxInfo),
192 NULL);
193
194 /* Does it need to be activated? */
195 if (!ActivationCtxInfo.hActCtx)
196 {
197 /* Activate it */
198 Status = RtlActivateActivationContextEx(1,
199 Teb,
200 ActivationCtxInfo.hActCtx,
201 &Cookie);
202 }
203 }
204 #endif
205
206 /* FIXME: Notify CSR */
207
208 /* Success */
209 if(lpThreadId) *lpThreadId = (DWORD)ClientId.UniqueThread;
210
211 /* Resume it if asked */
212 if (!(dwCreationFlags & CREATE_SUSPENDED))
213 {
214 NtResumeThread(hThread, &Dummy);
215 }
216
217 /* Return handle to thread */
218 return hThread;
219 }
220
221 /*
222 * @implemented
223 */
224 VOID
225 STDCALL
226 ExitThread(DWORD uExitCode)
227 {
228 NTSTATUS Status;
229 BOOLEAN LastThread;
230
231 /*
232 * Terminate process if this is the last thread
233 * of the current process
234 */
235 Status = NtQueryInformationThread(NtCurrentThread(),
236 ThreadAmILastThread,
237 &LastThread,
238 sizeof(BOOLEAN),
239 NULL);
240 if (NT_SUCCESS(Status) && LastThread)
241 {
242 /* Exit the Process */
243 ExitProcess(uExitCode);
244 }
245
246 /* Notify DLLs and TLS Callbacks of termination */
247 LdrShutdownThread();
248
249 /* Tell the Kernel to free the Stack */
250 NtCurrentTeb()->FreeStackOnTermination = TRUE;
251 NtTerminateThread(NULL, uExitCode);
252
253 /* We will never reach this place. This silences the compiler */
254 ExitThread(uExitCode);
255 }
256
257 /*
258 * @implemented
259 */
260 HANDLE
261 STDCALL
262 OpenThread(
263 DWORD dwDesiredAccess,
264 BOOL bInheritHandle,
265 DWORD dwThreadId
266 )
267 {
268 NTSTATUS errCode;
269 HANDLE ThreadHandle;
270 OBJECT_ATTRIBUTES ObjectAttributes;
271 CLIENT_ID ClientId ;
272
273 ClientId.UniqueProcess = 0;
274 ClientId.UniqueThread = (HANDLE)dwThreadId;
275
276 InitializeObjectAttributes (&ObjectAttributes,
277 NULL,
278 (bInheritHandle ? OBJ_INHERIT : 0),
279 NULL,
280 NULL);
281
282 errCode = NtOpenThread(&ThreadHandle,
283 dwDesiredAccess,
284 &ObjectAttributes,
285 &ClientId);
286 if (!NT_SUCCESS(errCode))
287 {
288 SetLastErrorByStatus (errCode);
289 return NULL;
290 }
291 return ThreadHandle;
292 }
293
294
295 /*
296 * @implemented
297 */
298 PTEB
299 GetTeb(VOID)
300 {
301 return(NtCurrentTeb());
302 }
303
304
305 /*
306 * @implemented
307 */
308 BOOL STDCALL
309 SwitchToThread(VOID)
310 {
311 NTSTATUS Status;
312 Status = NtYieldExecution();
313 return Status != STATUS_NO_YIELD_PERFORMED;
314 }
315
316
317 /*
318 * @implemented
319 */
320 DWORD STDCALL
321 GetCurrentThreadId(VOID)
322 {
323 return((DWORD)(NtCurrentTeb()->Cid).UniqueThread);
324 }
325
326 /*
327 * @implemented
328 */
329 BOOL STDCALL
330 GetThreadTimes(HANDLE hThread,
331 LPFILETIME lpCreationTime,
332 LPFILETIME lpExitTime,
333 LPFILETIME lpKernelTime,
334 LPFILETIME lpUserTime)
335 {
336 KERNEL_USER_TIMES KernelUserTimes;
337 NTSTATUS Status;
338
339 Status = NtQueryInformationThread(hThread,
340 ThreadTimes,
341 &KernelUserTimes,
342 sizeof(KERNEL_USER_TIMES),
343 NULL);
344 if (!NT_SUCCESS(Status))
345 {
346 SetLastErrorByStatus(Status);
347 return(FALSE);
348 }
349
350 lpCreationTime->dwLowDateTime = KernelUserTimes.CreateTime.u.LowPart;
351 lpCreationTime->dwHighDateTime = KernelUserTimes.CreateTime.u.HighPart;
352
353 lpExitTime->dwLowDateTime = KernelUserTimes.ExitTime.u.LowPart;
354 lpExitTime->dwHighDateTime = KernelUserTimes.ExitTime.u.HighPart;
355
356 lpKernelTime->dwLowDateTime = KernelUserTimes.KernelTime.u.LowPart;
357 lpKernelTime->dwHighDateTime = KernelUserTimes.KernelTime.u.HighPart;
358
359 lpUserTime->dwLowDateTime = KernelUserTimes.UserTime.u.LowPart;
360 lpUserTime->dwHighDateTime = KernelUserTimes.UserTime.u.HighPart;
361
362 return(TRUE);
363 }
364
365
366 /*
367 * @implemented
368 */
369 BOOL STDCALL
370 GetThreadContext(HANDLE hThread,
371 LPCONTEXT lpContext)
372 {
373 NTSTATUS Status;
374
375 Status = NtGetContextThread(hThread,
376 lpContext);
377 if (!NT_SUCCESS(Status))
378 {
379 SetLastErrorByStatus(Status);
380 return(FALSE);
381 }
382
383 return(TRUE);
384 }
385
386
387 /*
388 * @implemented
389 */
390 BOOL STDCALL
391 SetThreadContext(HANDLE hThread,
392 CONST CONTEXT *lpContext)
393 {
394 NTSTATUS Status;
395
396 Status = NtSetContextThread(hThread,
397 (void *)lpContext);
398 if (!NT_SUCCESS(Status))
399 {
400 SetLastErrorByStatus(Status);
401 return(FALSE);
402 }
403
404 return(TRUE);
405 }
406
407
408 /*
409 * @implemented
410 */
411 BOOL STDCALL
412 GetExitCodeThread(HANDLE hThread,
413 LPDWORD lpExitCode)
414 {
415 THREAD_BASIC_INFORMATION ThreadBasic;
416 NTSTATUS Status;
417
418 Status = NtQueryInformationThread(hThread,
419 ThreadBasicInformation,
420 &ThreadBasic,
421 sizeof(THREAD_BASIC_INFORMATION),
422 NULL);
423 if (!NT_SUCCESS(Status))
424 {
425 SetLastErrorByStatus(Status);
426 return(FALSE);
427 }
428
429 memcpy(lpExitCode, &ThreadBasic.ExitStatus, sizeof(DWORD));
430
431 return(TRUE);
432 }
433
434
435 /*
436 * @implemented
437 */
438 DWORD STDCALL
439 ResumeThread(HANDLE hThread)
440 {
441 ULONG PreviousResumeCount;
442 NTSTATUS Status;
443
444 Status = NtResumeThread(hThread,
445 &PreviousResumeCount);
446 if (!NT_SUCCESS(Status))
447 {
448 SetLastErrorByStatus(Status);
449 return(-1);
450 }
451
452 return(PreviousResumeCount);
453 }
454
455
456 /*
457 * @implemented
458 */
459 BOOL STDCALL
460 TerminateThread(HANDLE hThread,
461 DWORD dwExitCode)
462 {
463 NTSTATUS Status;
464
465 if (0 == hThread)
466 {
467 SetLastError(ERROR_INVALID_HANDLE);
468 return(FALSE);
469 }
470
471 Status = NtTerminateThread(hThread,
472 dwExitCode);
473 if (!NT_SUCCESS(Status))
474 {
475 SetLastErrorByStatus(Status);
476 return(FALSE);
477 }
478
479 return(TRUE);
480 }
481
482
483 /*
484 * @implemented
485 */
486 DWORD STDCALL
487 SuspendThread(HANDLE hThread)
488 {
489 ULONG PreviousSuspendCount;
490 NTSTATUS Status;
491
492 Status = NtSuspendThread(hThread,
493 &PreviousSuspendCount);
494 if (!NT_SUCCESS(Status))
495 {
496 SetLastErrorByStatus(Status);
497 return(-1);
498 }
499
500 return(PreviousSuspendCount);
501 }
502
503
504 /*
505 * @implemented
506 */
507 DWORD STDCALL
508 SetThreadAffinityMask(HANDLE hThread,
509 DWORD dwThreadAffinityMask)
510 {
511 THREAD_BASIC_INFORMATION ThreadBasic;
512 KAFFINITY AffinityMask;
513 NTSTATUS Status;
514
515 AffinityMask = (KAFFINITY)dwThreadAffinityMask;
516
517 Status = NtQueryInformationThread(hThread,
518 ThreadBasicInformation,
519 &ThreadBasic,
520 sizeof(THREAD_BASIC_INFORMATION),
521 NULL);
522 if (!NT_SUCCESS(Status))
523 {
524 SetLastErrorByStatus(Status);
525 return(0);
526 }
527
528 Status = NtSetInformationThread(hThread,
529 ThreadAffinityMask,
530 &AffinityMask,
531 sizeof(KAFFINITY));
532 if (!NT_SUCCESS(Status))
533 {
534 SetLastErrorByStatus(Status);
535 ThreadBasic.AffinityMask = 0;
536 }
537
538 return(ThreadBasic.AffinityMask);
539 }
540
541
542 /*
543 * @implemented
544 */
545 BOOL
546 STDCALL
547 SetThreadPriority(HANDLE hThread,
548 int nPriority)
549 {
550 ULONG Prio = nPriority;
551 NTSTATUS Status;
552
553 /* Check if values forcing saturation should be used */
554 if (Prio == THREAD_PRIORITY_TIME_CRITICAL)
555 {
556 Prio = (HIGH_PRIORITY + 1) / 2;
557 }
558 else if (Prio == THREAD_PRIORITY_IDLE)
559 {
560 Prio = -((HIGH_PRIORITY + 1) / 2);
561 }
562
563 /* Set the Base Priority */
564 Status = NtSetInformationThread(hThread,
565 ThreadBasePriority,
566 &Prio,
567 sizeof(ULONG));
568 if (!NT_SUCCESS(Status))
569 {
570 /* Failure */
571 SetLastErrorByStatus(Status);
572 return FALSE;
573 }
574
575 /* Return */
576 return TRUE;
577 }
578
579 /*
580 * @implemented
581 */
582 int
583 STDCALL
584 GetThreadPriority(HANDLE hThread)
585 {
586 THREAD_BASIC_INFORMATION ThreadBasic;
587 NTSTATUS Status;
588
589 /* Query the Base Priority Increment */
590 Status = NtQueryInformationThread(hThread,
591 ThreadBasicInformation,
592 &ThreadBasic,
593 sizeof(THREAD_BASIC_INFORMATION),
594 NULL);
595 if (!NT_SUCCESS(Status))
596 {
597 /* Failure */
598 SetLastErrorByStatus(Status);
599 return THREAD_PRIORITY_ERROR_RETURN;
600 }
601
602 /* Do some conversions for out of boundary values */
603 if (ThreadBasic.BasePriority > THREAD_BASE_PRIORITY_MAX)
604 {
605 ThreadBasic.BasePriority = THREAD_PRIORITY_TIME_CRITICAL;
606 }
607 else if (ThreadBasic.BasePriority < THREAD_BASE_PRIORITY_MIN)
608 {
609 ThreadBasic.BasePriority = THREAD_PRIORITY_IDLE;
610 }
611
612 /* Return the final result */
613 return ThreadBasic.BasePriority;
614 }
615
616 /*
617 * @implemented
618 */
619 BOOL STDCALL
620 GetThreadPriorityBoost(IN HANDLE hThread,
621 OUT PBOOL pDisablePriorityBoost)
622 {
623 ULONG PriorityBoost;
624 NTSTATUS Status;
625
626 Status = NtQueryInformationThread(hThread,
627 ThreadPriorityBoost,
628 &PriorityBoost,
629 sizeof(ULONG),
630 NULL);
631 if (!NT_SUCCESS(Status))
632 {
633 SetLastErrorByStatus(Status);
634 return(FALSE);
635 }
636
637 *pDisablePriorityBoost = !((BOOL)PriorityBoost);
638
639 return(TRUE);
640 }
641
642
643 /*
644 * @implemented
645 */
646 BOOL STDCALL
647 SetThreadPriorityBoost(IN HANDLE hThread,
648 IN BOOL bDisablePriorityBoost)
649 {
650 ULONG PriorityBoost;
651 NTSTATUS Status;
652
653 PriorityBoost = (ULONG)!bDisablePriorityBoost;
654
655 Status = NtSetInformationThread(hThread,
656 ThreadPriorityBoost,
657 &PriorityBoost,
658 sizeof(ULONG));
659 if (!NT_SUCCESS(Status))
660 {
661 SetLastErrorByStatus(Status);
662 return(FALSE);
663 }
664
665 return(TRUE);
666 }
667
668
669 /*
670 * @implemented
671 */
672 BOOL STDCALL
673 GetThreadSelectorEntry(IN HANDLE hThread,
674 IN DWORD dwSelector,
675 OUT LPLDT_ENTRY lpSelectorEntry)
676 {
677 DESCRIPTOR_TABLE_ENTRY DescriptionTableEntry;
678 NTSTATUS Status;
679
680 DescriptionTableEntry.Selector = dwSelector;
681 Status = NtQueryInformationThread(hThread,
682 ThreadDescriptorTableEntry,
683 &DescriptionTableEntry,
684 sizeof(DESCRIPTOR_TABLE_ENTRY),
685 NULL);
686 if(!NT_SUCCESS(Status))
687 {
688 SetLastErrorByStatus(Status);
689 return FALSE;
690 }
691
692 *lpSelectorEntry = DescriptionTableEntry.Descriptor;
693 return TRUE;
694 }
695
696
697 /*
698 * @implemented
699 */
700 DWORD STDCALL
701 SetThreadIdealProcessor(HANDLE hThread,
702 DWORD dwIdealProcessor)
703 {
704 NTSTATUS Status;
705
706 Status = NtSetInformationThread(hThread,
707 ThreadIdealProcessor,
708 &dwIdealProcessor,
709 sizeof(ULONG));
710 if (!NT_SUCCESS(Status))
711 {
712 SetLastErrorByStatus(Status);
713 return -1;
714 }
715
716 return dwIdealProcessor;
717 }
718
719
720 /*
721 * @implemented
722 */
723 DWORD STDCALL
724 GetProcessIdOfThread(HANDLE Thread)
725 {
726 THREAD_BASIC_INFORMATION ThreadBasic;
727 NTSTATUS Status;
728
729 Status = NtQueryInformationThread(Thread,
730 ThreadBasicInformation,
731 &ThreadBasic,
732 sizeof(THREAD_BASIC_INFORMATION),
733 NULL);
734 if(!NT_SUCCESS(Status))
735 {
736 SetLastErrorByStatus(Status);
737 return 0;
738 }
739
740 return (DWORD)ThreadBasic.ClientId.UniqueProcess;
741 }
742
743
744 /*
745 * @implemented
746 */
747 DWORD STDCALL
748 GetThreadId(HANDLE Thread)
749 {
750 THREAD_BASIC_INFORMATION ThreadBasic;
751 NTSTATUS Status;
752
753 Status = NtQueryInformationThread(Thread,
754 ThreadBasicInformation,
755 &ThreadBasic,
756 sizeof(THREAD_BASIC_INFORMATION),
757 NULL);
758 if(!NT_SUCCESS(Status))
759 {
760 SetLastErrorByStatus(Status);
761 return 0;
762 }
763
764 return (DWORD)ThreadBasic.ClientId.UniqueThread;
765 }
766
767 /*
768 * @unimplemented
769 */
770 LANGID STDCALL
771 SetThreadUILanguage(WORD wReserved)
772 {
773 DPRINT1("SetThreadUILanguage(0x%4x) unimplemented!\n", wReserved);
774 return 0;
775 }
776
777 static void CALLBACK
778 IntCallUserApc(PVOID Function, PVOID dwData, PVOID Argument3)
779 {
780 PAPCFUNC pfnAPC = (PAPCFUNC)Function;
781 pfnAPC((ULONG_PTR)dwData);
782 }
783
784 /*
785 * @implemented
786 */
787 DWORD STDCALL
788 QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
789 {
790 NTSTATUS Status;
791
792 Status = NtQueueApcThread(hThread, IntCallUserApc, pfnAPC,
793 (PVOID)dwData, NULL);
794 if (Status)
795 SetLastErrorByStatus(Status);
796
797 return NT_SUCCESS(Status);
798 }
799
800 /*
801 * @implemented
802 */
803 BOOL STDCALL
804 GetThreadIOPendingFlag(HANDLE hThread,
805 PBOOL lpIOIsPending)
806 {
807 ULONG IoPending;
808 NTSTATUS Status;
809
810 if(lpIOIsPending == NULL)
811 {
812 SetLastError(ERROR_INVALID_PARAMETER);
813 return FALSE;
814 }
815
816 Status = NtQueryInformationThread(hThread,
817 ThreadIsIoPending,
818 (PVOID)&IoPending,
819 sizeof(IoPending),
820 NULL);
821 if(NT_SUCCESS(Status))
822 {
823 *lpIOIsPending = ((IoPending != 0) ? TRUE : FALSE);
824 return TRUE;
825 }
826
827 SetLastErrorByStatus(Status);
828 return FALSE;
829 }
830
831
832 /*
833 * @implemented
834 */
835 VOID STDCALL
836 Sleep(DWORD dwMilliseconds)
837 {
838 SleepEx(dwMilliseconds, FALSE);
839 return;
840 }
841
842
843 /*
844 * @implemented
845 */
846 DWORD STDCALL
847 SleepEx(DWORD dwMilliseconds,
848 BOOL bAlertable)
849 {
850 LARGE_INTEGER Interval;
851 NTSTATUS errCode;
852
853 if (dwMilliseconds != INFINITE)
854 {
855 /*
856 * System time units are 100 nanoseconds (a nanosecond is a billionth of
857 * a second).
858 */
859 Interval.QuadPart = -((ULONGLONG)dwMilliseconds * 10000);
860 }
861 else
862 {
863 /* Approximately 292000 years hence */
864 Interval.QuadPart = -0x7FFFFFFFFFFFFFFFLL;
865 }
866
867 errCode = NtDelayExecution ((bAlertable ? TRUE : FALSE), &Interval);
868 if (!NT_SUCCESS(errCode))
869 {
870 SetLastErrorByStatus (errCode);
871 return -1;
872 }
873 return 0;
874 }
875
876 /* EOF */