xref: /reactos/dll/win32/kernel32/client/thread.c (revision cf77354d)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            dll/win32/kernel32/client/thread.c
5  * PURPOSE:         Thread functions
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Ariadne (ariadne@xs4all.nl)
8  *
9  */
10 
11 /* INCLUDES *******************************************************************/
12 
13 #include <k32.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 #define SXS_SUPPORT_FIXME
19 
20 typedef NTSTATUS (NTAPI *PCSR_CREATE_REMOTE_THREAD)(IN HANDLE ThreadHandle, IN PCLIENT_ID ClientId);
21 
22 NTSTATUS
23 WINAPI
24 BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,
25                        IN PCLIENT_ID ClientId);
26 
27 /* FUNCTIONS ******************************************************************/
28 
29 static
30 LONG BaseThreadExceptionFilter(EXCEPTION_POINTERS * ExceptionInfo)
31 {
32     LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
33     LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
34 
35     RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
36     if (RealFilter != NULL)
37     {
38         _SEH2_TRY
39         {
40             ExceptionDisposition = RealFilter(ExceptionInfo);
41         }
42         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
43         {
44             ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
45         }
46         _SEH2_END;
47     }
48 
49     return ExceptionDisposition;
50 }
51 
52 __declspec(noreturn)
53 VOID
54 WINAPI
55 BaseThreadStartup(IN LPTHREAD_START_ROUTINE lpStartAddress,
56                   IN LPVOID lpParameter)
57 {
58     /* Attempt to call the Thread Start Address */
59     _SEH2_TRY
60     {
61         /* Legacy check which is still used today for Win32 threads */
62         if (NtCurrentTeb()->NtTib.Version == (30 << 8)) // OS/2 V3.0 ("Cruiser")
63         {
64             /* This registers the termination port with CSRSS */
65             if (!BaseRunningInServerProcess) CsrNewThread();
66         }
67 
68         /* Get the exit code from the Thread Start */
69         ExitThread((lpStartAddress)((PVOID)lpParameter));
70     }
71     _SEH2_EXCEPT(BaseThreadExceptionFilter(_SEH2_GetExceptionInformation()))
72     {
73         /* Get the Exit code from the SEH Handler */
74         if (!BaseRunningInServerProcess)
75         {
76             /* Kill the whole process, usually */
77             ExitProcess(_SEH2_GetExceptionCode());
78         }
79         else
80         {
81             /* If running inside CSRSS, kill just this thread */
82             ExitThread(_SEH2_GetExceptionCode());
83         }
84     }
85     _SEH2_END;
86 }
87 
88 VOID
89 NTAPI
90 BaseDispatchApc(IN PAPCFUNC ApcRoutine,
91                 IN PVOID Data,
92                 IN PACTIVATION_CONTEXT ActivationContext)
93 {
94     RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame;
95 
96     /* Setup the activation context */
97     ActivationFrame.Size = sizeof(ActivationFrame);
98     ActivationFrame.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
99 
100     /* Check if caller wanted one */
101     if (ActivationContext == INVALID_ACTIVATION_CONTEXT)
102     {
103         /* Do the APC directly */
104         ApcRoutine((ULONG_PTR)Data);
105         return;
106     }
107 
108     /* Then activate it */
109     RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext);
110 
111     /* Call the routine under SEH */
112     _SEH2_TRY
113     {
114         ApcRoutine((ULONG_PTR)Data);
115     }
116     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
117     {
118 
119     }
120     _SEH2_END;
121 
122     /* Now de-activate and release the activation context */
123     RtlDeactivateActivationContextUnsafeFast(&ActivationFrame);
124     RtlReleaseActivationContext(ActivationContext);
125 }
126 
127 /* PUBLIC FUNCTIONS ***********************************************************/
128 
129 /*
130  * @implemented
131  */
132 HANDLE
133 WINAPI
134 DECLSPEC_HOTPATCH
135 CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
136              IN DWORD dwStackSize,
137              IN LPTHREAD_START_ROUTINE lpStartAddress,
138              IN LPVOID lpParameter,
139              IN DWORD dwCreationFlags,
140              OUT LPDWORD lpThreadId)
141 {
142     /* Act as if we're going to create a remote thread in ourselves */
143     return CreateRemoteThread(NtCurrentProcess(),
144                               lpThreadAttributes,
145                               dwStackSize,
146                               lpStartAddress,
147                               lpParameter,
148                               dwCreationFlags,
149                               lpThreadId);
150 }
151 
152 /*
153  * @implemented
154  */
155 HANDLE
156 WINAPI
157 CreateRemoteThread(IN HANDLE hProcess,
158                    IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
159                    IN DWORD dwStackSize,
160                    IN LPTHREAD_START_ROUTINE lpStartAddress,
161                    IN LPVOID lpParameter,
162                    IN DWORD dwCreationFlags,
163                    OUT LPDWORD lpThreadId)
164 {
165     NTSTATUS Status;
166     INITIAL_TEB InitialTeb;
167     CONTEXT Context;
168     CLIENT_ID ClientId;
169     OBJECT_ATTRIBUTES LocalObjectAttributes;
170     POBJECT_ATTRIBUTES ObjectAttributes;
171     HANDLE hThread;
172     ULONG Dummy;
173     PTEB Teb;
174     THREAD_BASIC_INFORMATION ThreadBasicInfo;
175     PACTIVATION_CONTEXT_STACK ActivationContextStack = NULL;
176     ACTIVATION_CONTEXT_BASIC_INFORMATION ActCtxInfo;
177     ULONG_PTR Cookie;
178     ULONG ReturnLength;
179     SIZE_T ReturnSize;
180     DPRINT("CreateRemoteThread: hProcess: %p dwStackSize: %lu lpStartAddress"
181             ": %p lpParameter: %p, dwCreationFlags: %lx\n", hProcess,
182             dwStackSize, lpStartAddress, lpParameter, dwCreationFlags);
183 
184     /* Clear the Context */
185     RtlZeroMemory(&Context, sizeof(CONTEXT));
186 
187     /* Write PID */
188     ClientId.UniqueProcess = hProcess;
189 
190     /* Create the Stack */
191     Status = BaseCreateStack(hProcess,
192                              dwStackSize,
193                              dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION ?
194                              dwStackSize : 0,
195                              &InitialTeb);
196     if (!NT_SUCCESS(Status))
197     {
198         BaseSetLastNTError(Status);
199         return NULL;
200     }
201 
202     /* Create Initial Context */
203     BaseInitializeContext(&Context,
204                           lpParameter,
205                           lpStartAddress,
206                           InitialTeb.StackBase,
207                           1);
208 
209     /* initialize the attributes for the thread object */
210     ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
211                                                   lpThreadAttributes,
212                                                   NULL);
213 
214     /* Create the Kernel Thread Object */
215     Status = NtCreateThread(&hThread,
216                             THREAD_ALL_ACCESS,
217                             ObjectAttributes,
218                             hProcess,
219                             &ClientId,
220                             &Context,
221                             &InitialTeb,
222                             TRUE);
223     if (!NT_SUCCESS(Status))
224     {
225         /* Fail the kernel create */
226         BaseFreeThreadStack(hProcess, &InitialTeb);
227         BaseSetLastNTError(Status);
228         return NULL;
229     }
230 
231     /* Are we in the same process? */
232     if (hProcess == NtCurrentProcess())
233     {
234         /* Get the TEB */
235         Status = NtQueryInformationThread(hThread,
236                                           ThreadBasicInformation,
237                                           &ThreadBasicInfo,
238                                           sizeof(ThreadBasicInfo),
239                                           &ReturnLength);
240         if (!NT_SUCCESS(Status))
241         {
242             /* Fail */
243             ERROR_DBGBREAK("SXS: %s - Failing thread create because "
244                            "NtQueryInformationThread() failed with status %08lx\n",
245                            __FUNCTION__, Status);
246             return NULL;
247         }
248 
249         /* Allocate the Activation Context Stack */
250         Status = RtlAllocateActivationContextStack(&ActivationContextStack);
251         if (!NT_SUCCESS(Status))
252         {
253             /* Fail */
254             ERROR_DBGBREAK("SXS: %s - Failing thread create because "
255                            "RtlAllocateActivationContextStack() failed with status %08lx\n",
256                            __FUNCTION__, Status);
257             return NULL;
258         }
259 
260         /* Save it */
261         Teb = ThreadBasicInfo.TebBaseAddress;
262         Teb->ActivationContextStackPointer = ActivationContextStack;
263 
264         /* Query the Context */
265         Status = RtlQueryInformationActivationContext(RTL_QUERY_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT,
266                                                       NULL,
267                                                       0,
268                                                       ActivationContextBasicInformation,
269                                                       &ActCtxInfo,
270                                                       sizeof(ActCtxInfo),
271                                                       &ReturnSize);
272         if (!NT_SUCCESS(Status))
273         {
274             /* Fail */
275             ERROR_DBGBREAK("SXS: %s - Failing thread create because "
276                            "RtlQueryInformationActivationContext() failed with status %08lx\n",
277                            __FUNCTION__, Status);
278 
279             /* Free the activation context stack */
280             // RtlFreeThreadActivationContextStack();
281             RtlFreeActivationContextStack(Teb->ActivationContextStackPointer);
282 
283             return NULL;
284         }
285 
286         /* Does it need to be activated? */
287         if ((ActCtxInfo.hActCtx) && !(ActCtxInfo.dwFlags & 1))
288         {
289             /* Activate it */
290             Status = RtlActivateActivationContextEx(RTL_ACTIVATE_ACTIVATION_CONTEXT_EX_FLAG_RELEASE_ON_STACK_DEALLOCATION,
291                                                     Teb,
292                                                     ActCtxInfo.hActCtx,
293                                                     &Cookie);
294             if (!NT_SUCCESS(Status))
295             {
296                 /* Fail */
297                 ERROR_DBGBREAK("SXS: %s - Failing thread create because "
298                                "RtlActivateActivationContextEx() failed with status %08lx\n",
299                                __FUNCTION__, Status);
300 
301                 /* Free the activation context stack */
302                 // RtlFreeThreadActivationContextStack();
303                 RtlFreeActivationContextStack(Teb->ActivationContextStackPointer);
304 
305                 return NULL;
306             }
307         }
308     }
309 
310     /* Notify CSR */
311     if (!BaseRunningInServerProcess)
312     {
313         Status = BasepNotifyCsrOfThread(hThread, &ClientId);
314         ASSERT(NT_SUCCESS(Status));
315     }
316     else
317     {
318         if (hProcess != NtCurrentProcess())
319         {
320             PCSR_CREATE_REMOTE_THREAD CsrCreateRemoteThread;
321 
322             /* Get the direct CSRSRV export */
323             CsrCreateRemoteThread = (PCSR_CREATE_REMOTE_THREAD)
324                                     GetProcAddress(GetModuleHandleA("csrsrv"),
325                                                    "CsrCreateRemoteThread");
326             if (CsrCreateRemoteThread)
327             {
328                 /* Call it instead of going through LPC */
329                 Status = CsrCreateRemoteThread(hThread, &ClientId);
330                 ASSERT(NT_SUCCESS(Status));
331             }
332         }
333     }
334 
335     /* Success */
336     if (lpThreadId) *lpThreadId = HandleToUlong(ClientId.UniqueThread);
337 
338     /* Resume it if asked */
339     if (!(dwCreationFlags & CREATE_SUSPENDED)) NtResumeThread(hThread, &Dummy);
340 
341     /* Return handle to thread */
342     return hThread;
343 }
344 
345 /*
346  * @implemented
347  */
348 VOID
349 WINAPI
350 ExitThread(IN DWORD uExitCode)
351 {
352     NTSTATUS Status;
353     ULONG LastThread;
354     PRTL_CRITICAL_SECTION LoaderLock;
355 
356     /* Make sure loader lock isn't held */
357     LoaderLock = NtCurrentPeb()->LoaderLock;
358     if (LoaderLock) ASSERT(NtCurrentTeb()->ClientId.UniqueThread != LoaderLock->OwningThread);
359 
360     /*
361      * Terminate process if this is the last thread
362      * of the current process
363      */
364     Status = NtQueryInformationThread(NtCurrentThread(),
365                                       ThreadAmILastThread,
366                                       &LastThread,
367                                       sizeof(LastThread),
368                                       NULL);
369     if ((NT_SUCCESS(Status)) && (LastThread)) ExitProcess(uExitCode);
370 
371     /* Notify DLLs and TLS Callbacks of termination */
372     LdrShutdownThread();
373 
374     /* Tell the Kernel to free the Stack */
375     NtCurrentTeb()->FreeStackOnTermination = TRUE;
376     NtTerminateThread(NULL, uExitCode);
377 
378     /* We should never reach this place */
379     ERROR_FATAL("It should not happen\n");
380     while (TRUE); /* 'noreturn' function */
381 }
382 
383 /*
384  * @implemented
385  */
386 HANDLE
387 WINAPI
388 OpenThread(IN DWORD dwDesiredAccess,
389            IN BOOL bInheritHandle,
390            IN DWORD dwThreadId)
391 {
392     NTSTATUS Status;
393     HANDLE ThreadHandle;
394     OBJECT_ATTRIBUTES ObjectAttributes;
395     CLIENT_ID ClientId;
396 
397     ClientId.UniqueProcess = 0;
398     ClientId.UniqueThread = ULongToHandle(dwThreadId);
399 
400     InitializeObjectAttributes(&ObjectAttributes,
401                                NULL,
402                                (bInheritHandle ? OBJ_INHERIT : 0),
403                                NULL,
404                                NULL);
405 
406     Status = NtOpenThread(&ThreadHandle,
407                           dwDesiredAccess,
408                           &ObjectAttributes,
409                           &ClientId);
410     if (!NT_SUCCESS(Status))
411     {
412         BaseSetLastNTError(Status);
413         return NULL;
414     }
415 
416     return ThreadHandle;
417 }
418 
419 /*
420  * @implemented
421  */
422 PTEB
423 GetTeb(VOID)
424 {
425     return NtCurrentTeb();
426 }
427 
428 /*
429  * @implemented
430  */
431 BOOL
432 WINAPI
433 SwitchToThread(VOID)
434 {
435     return NtYieldExecution() != STATUS_NO_YIELD_PERFORMED;
436 }
437 
438 
439 /*
440  * @implemented
441  */
442 DWORD
443 WINAPI
444 GetCurrentThreadId(VOID)
445 {
446     return HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
447 }
448 
449 /*
450  * @implemented
451  */
452 BOOL
453 NTAPI
454 GetThreadTimes(IN HANDLE hThread,
455                OUT LPFILETIME lpCreationTime,
456                OUT LPFILETIME lpExitTime,
457                OUT LPFILETIME lpKernelTime,
458                OUT LPFILETIME lpUserTime)
459 {
460     KERNEL_USER_TIMES KernelUserTimes;
461     NTSTATUS Status;
462 
463     Status = NtQueryInformationThread(hThread,
464                                       ThreadTimes,
465                                       &KernelUserTimes,
466                                       sizeof(KERNEL_USER_TIMES),
467                                       NULL);
468     if (!NT_SUCCESS(Status))
469     {
470         BaseSetLastNTError(Status);
471         return FALSE;
472     }
473 
474     *lpCreationTime = *(LPFILETIME)&KernelUserTimes.CreateTime;
475     *lpExitTime = *(LPFILETIME)&KernelUserTimes.ExitTime;
476     *lpKernelTime = *(LPFILETIME)&KernelUserTimes.KernelTime;
477     *lpUserTime = *(LPFILETIME)&KernelUserTimes.UserTime;
478     return TRUE;
479 }
480 
481 /*
482  * @implemented
483  */
484 BOOL
485 WINAPI
486 GetThreadContext(IN HANDLE hThread,
487                  OUT LPCONTEXT lpContext)
488 {
489     NTSTATUS Status;
490 
491     Status = NtGetContextThread(hThread, lpContext);
492     if (!NT_SUCCESS(Status))
493     {
494         BaseSetLastNTError(Status);
495         return FALSE;
496     }
497 
498     return TRUE;
499 }
500 
501 /*
502  * @implemented
503  */
504 BOOL
505 WINAPI
506 SetThreadContext(IN HANDLE hThread,
507                  IN CONST CONTEXT *lpContext)
508 {
509     NTSTATUS Status;
510 
511     Status = NtSetContextThread(hThread, (PCONTEXT)lpContext);
512     if (!NT_SUCCESS(Status))
513     {
514         BaseSetLastNTError(Status);
515         return FALSE;
516     }
517 
518     return TRUE;
519 }
520 
521 /*
522  * @implemented
523  */
524 BOOL
525 WINAPI
526 GetExitCodeThread(IN HANDLE hThread,
527                   OUT LPDWORD lpExitCode)
528 {
529     THREAD_BASIC_INFORMATION ThreadBasic;
530     NTSTATUS Status;
531 
532     Status = NtQueryInformationThread(hThread,
533                                       ThreadBasicInformation,
534                                       &ThreadBasic,
535                                       sizeof(THREAD_BASIC_INFORMATION),
536                                       NULL);
537     if (!NT_SUCCESS(Status))
538     {
539         BaseSetLastNTError(Status);
540         return FALSE;
541     }
542 
543     *lpExitCode = ThreadBasic.ExitStatus;
544     return TRUE;
545 }
546 
547 /*
548  * @implemented
549  */
550 DWORD
551 WINAPI
552 ResumeThread(IN HANDLE hThread)
553 {
554     ULONG PreviousResumeCount;
555     NTSTATUS Status;
556 
557     Status = NtResumeThread(hThread, &PreviousResumeCount);
558     if (!NT_SUCCESS(Status))
559     {
560         BaseSetLastNTError(Status);
561         return -1;
562     }
563 
564     return PreviousResumeCount;
565 }
566 
567 /*
568  * @implemented
569  */
570 BOOL
571 WINAPI
572 TerminateThread(IN HANDLE hThread,
573                 IN DWORD dwExitCode)
574 {
575     NTSTATUS Status;
576 #if DBG
577     PRTL_CRITICAL_SECTION LoaderLock;
578     THREAD_BASIC_INFORMATION ThreadInfo;
579 #endif /* DBG */
580 
581     /* Check for invalid thread handle */
582     if (!hThread)
583     {
584         /* Fail if one was passed */
585         SetLastError(ERROR_INVALID_HANDLE);
586         return FALSE;
587     }
588 
589 #if DBG
590     /* Get the loader lock */
591     LoaderLock = NtCurrentPeb()->LoaderLock;
592     if (LoaderLock)
593     {
594         /* Get our TID */
595         Status = NtQueryInformationThread(hThread,
596                                           ThreadBasicInformation,
597                                           &ThreadInfo,
598                                           sizeof(ThreadInfo),
599                                           NULL);
600         if (NT_SUCCESS(Status))
601         {
602             /* If terminating the current thread, we must not hold the loader lock */
603             if (NtCurrentTeb()->ClientId.UniqueThread == ThreadInfo.ClientId.UniqueThread)
604                 ASSERT(NtCurrentTeb()->ClientId.UniqueThread != LoaderLock->OwningThread);
605         }
606     }
607 #endif /* DBG */
608 
609     /* Now terminate the thread */
610     Status = NtTerminateThread(hThread, dwExitCode);
611     if (!NT_SUCCESS(Status))
612     {
613         /* Fail */
614         BaseSetLastNTError(Status);
615         return FALSE;
616     }
617 
618     /* All done */
619     return TRUE;
620 }
621 
622 /*
623  * @implemented
624  */
625 DWORD
626 WINAPI
627 SuspendThread(IN HANDLE hThread)
628 {
629     ULONG PreviousSuspendCount;
630     NTSTATUS Status;
631 
632     Status = NtSuspendThread(hThread, &PreviousSuspendCount);
633     if (!NT_SUCCESS(Status))
634     {
635         BaseSetLastNTError(Status);
636         return -1;
637     }
638 
639     return PreviousSuspendCount;
640 }
641 
642 /*
643  * @implemented
644  */
645 DWORD_PTR
646 WINAPI
647 SetThreadAffinityMask(IN HANDLE hThread,
648                       IN DWORD_PTR dwThreadAffinityMask)
649 {
650     THREAD_BASIC_INFORMATION ThreadBasic;
651     KAFFINITY AffinityMask;
652     NTSTATUS Status;
653 
654     AffinityMask = (KAFFINITY)dwThreadAffinityMask;
655 
656     Status = NtQueryInformationThread(hThread,
657                                       ThreadBasicInformation,
658                                       &ThreadBasic,
659                                       sizeof(THREAD_BASIC_INFORMATION),
660                                       NULL);
661     if (!NT_SUCCESS(Status))
662     {
663         BaseSetLastNTError(Status);
664         return 0;
665     }
666 
667     Status = NtSetInformationThread(hThread,
668                                     ThreadAffinityMask,
669                                     &AffinityMask,
670                                     sizeof(KAFFINITY));
671     if (!NT_SUCCESS(Status))
672     {
673         BaseSetLastNTError(Status);
674         ThreadBasic.AffinityMask = 0;
675     }
676 
677     return ThreadBasic.AffinityMask;
678 }
679 
680 /*
681  * @implemented
682  */
683 BOOL
684 WINAPI
685 SetThreadPriority(IN HANDLE hThread,
686                   IN int nPriority)
687 {
688     LONG Prio = nPriority;
689     NTSTATUS Status;
690 
691     /* Check if values forcing saturation should be used */
692     if (Prio == THREAD_PRIORITY_TIME_CRITICAL)
693     {
694         /* This is 16 */
695         Prio = (HIGH_PRIORITY + 1) / 2;
696     }
697     else if (Prio == THREAD_PRIORITY_IDLE)
698     {
699         /* This is -16 */
700         Prio = -((HIGH_PRIORITY + 1) / 2);
701     }
702 
703     /* Set the Base Priority */
704     Status = NtSetInformationThread(hThread,
705                                     ThreadBasePriority,
706                                     &Prio,
707                                     sizeof(LONG));
708     if (!NT_SUCCESS(Status))
709     {
710         /* Failure */
711         BaseSetLastNTError(Status);
712         return FALSE;
713     }
714 
715     /* Return */
716     return TRUE;
717 }
718 
719 /*
720  * @implemented
721  */
722 int
723 WINAPI
724 GetThreadPriority(IN HANDLE hThread)
725 {
726     THREAD_BASIC_INFORMATION ThreadBasic;
727     NTSTATUS Status;
728 
729     /* Query the Base Priority Increment */
730     Status = NtQueryInformationThread(hThread,
731                                       ThreadBasicInformation,
732                                       &ThreadBasic,
733                                       sizeof(THREAD_BASIC_INFORMATION),
734                                       NULL);
735     if (!NT_SUCCESS(Status))
736     {
737         /* Failure */
738         BaseSetLastNTError(Status);
739         return THREAD_PRIORITY_ERROR_RETURN;
740     }
741 
742     /* Do some conversions for saturation values */
743     if (ThreadBasic.BasePriority == ((HIGH_PRIORITY + 1) / 2))
744     {
745         /* Win32 calls this "time critical" */
746         ThreadBasic.BasePriority = THREAD_PRIORITY_TIME_CRITICAL;
747     }
748     else if (ThreadBasic.BasePriority == -((HIGH_PRIORITY + 1) / 2))
749     {
750         /* Win32 calls this "idle" */
751         ThreadBasic.BasePriority = THREAD_PRIORITY_IDLE;
752     }
753 
754     /* Return the final result */
755     return ThreadBasic.BasePriority;
756 }
757 
758 /*
759  * @implemented
760  */
761 BOOL
762 WINAPI
763 GetThreadPriorityBoost(IN HANDLE hThread,
764                        OUT PBOOL pDisablePriorityBoost)
765 {
766     ULONG PriorityBoost;
767     NTSTATUS Status;
768 
769     Status = NtQueryInformationThread(hThread,
770                                       ThreadPriorityBoost,
771                                       &PriorityBoost,
772                                       sizeof(ULONG),
773                                       NULL);
774     if (!NT_SUCCESS(Status))
775     {
776         BaseSetLastNTError(Status);
777         return FALSE;
778     }
779 
780     *pDisablePriorityBoost = PriorityBoost;
781     return TRUE;
782 }
783 
784 /*
785  * @implemented
786  */
787 BOOL
788 NTAPI
789 SetThreadPriorityBoost(IN HANDLE hThread,
790                        IN BOOL bDisablePriorityBoost)
791 {
792     ULONG PriorityBoost;
793     NTSTATUS Status;
794 
795     PriorityBoost = bDisablePriorityBoost != FALSE;
796 
797     Status = NtSetInformationThread(hThread,
798                                     ThreadPriorityBoost,
799                                     &PriorityBoost,
800                                     sizeof(ULONG));
801     if (!NT_SUCCESS(Status))
802     {
803         BaseSetLastNTError(Status);
804         return FALSE;
805     }
806 
807     return TRUE;
808 }
809 
810 /*
811  * @implemented
812  */
813 BOOL
814 WINAPI
815 GetThreadSelectorEntry(IN HANDLE hThread,
816                        IN DWORD dwSelector,
817                        OUT LPLDT_ENTRY lpSelectorEntry)
818 {
819 #ifdef _M_IX86
820     DESCRIPTOR_TABLE_ENTRY DescriptionTableEntry;
821     NTSTATUS Status;
822 
823     /* Set the selector and do the query */
824     DescriptionTableEntry.Selector = dwSelector;
825     Status = NtQueryInformationThread(hThread,
826                                       ThreadDescriptorTableEntry,
827                                       &DescriptionTableEntry,
828                                       sizeof(DESCRIPTOR_TABLE_ENTRY),
829                                       NULL);
830     if (!NT_SUCCESS(Status))
831     {
832         /* Fail */
833         BaseSetLastNTError(Status);
834         return FALSE;
835     }
836 
837     /* Success, return the selector */
838     *lpSelectorEntry = DescriptionTableEntry.Descriptor;
839     return TRUE;
840 #else
841     DPRINT1("Calling GetThreadSelectorEntry!\n");
842     return FALSE;
843 #endif
844 }
845 
846 /*
847  * @implemented
848  */
849 DWORD
850 WINAPI
851 SetThreadIdealProcessor(IN HANDLE hThread,
852                         IN DWORD dwIdealProcessor)
853 {
854     NTSTATUS Status;
855 
856     Status = NtSetInformationThread(hThread,
857                                     ThreadIdealProcessor,
858                                     &dwIdealProcessor,
859                                     sizeof(ULONG));
860     if (!NT_SUCCESS(Status))
861     {
862         BaseSetLastNTError(Status);
863         return -1;
864     }
865 
866     return (DWORD)Status;
867 }
868 
869 /*
870  * @implemented
871  */
872 DWORD
873 WINAPI
874 GetProcessIdOfThread(IN HANDLE Thread)
875 {
876     THREAD_BASIC_INFORMATION ThreadBasic;
877     NTSTATUS Status;
878 
879     Status = NtQueryInformationThread(Thread,
880                                       ThreadBasicInformation,
881                                       &ThreadBasic,
882                                       sizeof(THREAD_BASIC_INFORMATION),
883                                       NULL);
884     if (!NT_SUCCESS(Status))
885     {
886         BaseSetLastNTError(Status);
887         return 0;
888     }
889 
890     return HandleToUlong(ThreadBasic.ClientId.UniqueProcess);
891 }
892 
893 /*
894  * @implemented
895  */
896 DWORD
897 WINAPI
898 GetThreadId(IN HANDLE Thread)
899 {
900     THREAD_BASIC_INFORMATION ThreadBasic;
901     NTSTATUS Status;
902 
903     Status = NtQueryInformationThread(Thread,
904                                       ThreadBasicInformation,
905                                       &ThreadBasic,
906                                       sizeof(THREAD_BASIC_INFORMATION),
907                                       NULL);
908     if (!NT_SUCCESS(Status))
909     {
910         BaseSetLastNTError(Status);
911         return 0;
912     }
913 
914     return HandleToUlong(ThreadBasic.ClientId.UniqueThread);
915 }
916 
917 /*
918  * @unimplemented
919  */
920 LANGID
921 WINAPI
922 SetThreadUILanguage(IN LANGID LangId)
923 {
924     UNIMPLEMENTED;
925     return (LANGID)NtCurrentTeb()->CurrentLocale;
926 }
927 
928 /*
929  * @implemented
930  */
931 DWORD
932 WINAPI
933 QueueUserAPC(IN PAPCFUNC pfnAPC,
934              IN HANDLE hThread,
935              IN ULONG_PTR dwData)
936 {
937     NTSTATUS Status;
938     ACTIVATION_CONTEXT_BASIC_INFORMATION ActCtxInfo;
939 
940     /* Zero the activation context and query information on it */
941     RtlZeroMemory(&ActCtxInfo, sizeof(ActCtxInfo));
942     Status = RtlQueryInformationActivationContext(RTL_QUERY_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT,
943                                                   NULL,
944                                                   0,
945                                                   ActivationContextBasicInformation,
946                                                   &ActCtxInfo,
947                                                   sizeof(ActCtxInfo),
948                                                   NULL);
949     if (!NT_SUCCESS(Status))
950     {
951         /* Fail due to SxS */
952         DbgPrint("SXS: %s failing because RtlQueryInformationActivationContext()"
953                  "returned status %08lx\n", __FUNCTION__, Status);
954         BaseSetLastNTError(Status);
955         return FALSE;
956     }
957 
958     /* Queue the APC */
959     Status = NtQueueApcThread(hThread,
960                               (PKNORMAL_ROUTINE)BaseDispatchApc,
961                               pfnAPC,
962                               (PVOID)dwData,
963                               (ActCtxInfo.dwFlags & 1) ?
964                               INVALID_ACTIVATION_CONTEXT : ActCtxInfo.hActCtx);
965     if (!NT_SUCCESS(Status))
966     {
967         BaseSetLastNTError(Status);
968         return FALSE;
969     }
970 
971     /* All good */
972     return TRUE;
973 }
974 
975 /*
976  * @implemented
977  */
978 BOOL
979 WINAPI
980 SetThreadStackGuarantee(IN OUT PULONG StackSizeInBytes)
981 {
982     static int once;
983     if (once++ == 0)
984          DPRINT1("SetThreadStackGuarantee(%p): stub\n", StackSizeInBytes);
985     return TRUE;
986 }
987 
988 /*
989  * @implemented
990  */
991 BOOL
992 WINAPI
993 GetThreadIOPendingFlag(IN HANDLE hThread,
994                        OUT PBOOL lpIOIsPending)
995 {
996     ULONG IoPending;
997     NTSTATUS Status;
998 
999     /* Query the flag */
1000     Status = NtQueryInformationThread(hThread,
1001                                       ThreadIsIoPending,
1002                                       &IoPending,
1003                                       sizeof(IoPending),
1004                                       NULL);
1005     if (NT_SUCCESS(Status))
1006     {
1007         /* Return the flag */
1008         *lpIOIsPending = IoPending ? TRUE : FALSE;
1009         return TRUE;
1010     }
1011 
1012     /* Fail */
1013     BaseSetLastNTError(Status);
1014     return FALSE;
1015 }
1016 
1017 /*
1018  * @implemented
1019  */
1020 BOOL
1021 WINAPI
1022 QueueUserWorkItem(IN LPTHREAD_START_ROUTINE Function,
1023                   IN PVOID Context,
1024                   IN ULONG Flags)
1025 {
1026     NTSTATUS Status;
1027 
1028     /* NOTE: Rtl needs to safely call the function using a trampoline */
1029     Status = RtlQueueWorkItem((WORKERCALLBACKFUNC)Function, Context, Flags);
1030     if (!NT_SUCCESS(Status))
1031     {
1032         /* Failed */
1033         BaseSetLastNTError(Status);
1034         return FALSE;
1035     }
1036 
1037     /* All good */
1038     return TRUE;
1039 }
1040 
1041 /*
1042  * @implemented
1043  */
1044 DWORD
1045 WINAPI
1046 TlsAlloc(VOID)
1047 {
1048     ULONG Index;
1049     PTEB Teb;
1050     PPEB Peb;
1051 
1052     /* Get the PEB and TEB, lock the PEB */
1053     Teb = NtCurrentTeb();
1054     Peb = Teb->ProcessEnvironmentBlock;
1055     RtlAcquirePebLock();
1056 
1057     /* Try to get regular TEB slot */
1058     Index = RtlFindClearBitsAndSet(Peb->TlsBitmap, 1, 0);
1059     if (Index != 0xFFFFFFFF)
1060     {
1061         /* Clear the value. */
1062         Teb->TlsSlots[Index] = 0;
1063         RtlReleasePebLock();
1064         return Index;
1065     }
1066 
1067     /* If it fails, try to find expansion TEB slot. */
1068     Index = RtlFindClearBitsAndSet(Peb->TlsExpansionBitmap, 1, 0);
1069     if (Index != 0xFFFFFFFF)
1070     {
1071         /* Is there no expansion slot yet? */
1072         if (!Teb->TlsExpansionSlots)
1073         {
1074             /* Allocate an array */
1075             Teb->TlsExpansionSlots = RtlAllocateHeap(RtlGetProcessHeap(),
1076                                                      HEAP_ZERO_MEMORY,
1077                                                      TLS_EXPANSION_SLOTS *
1078                                                      sizeof(PVOID));
1079         }
1080 
1081         /* Did we get an array? */
1082         if (!Teb->TlsExpansionSlots)
1083         {
1084             /* Fail */
1085             RtlClearBits(Peb->TlsExpansionBitmap, Index, 1);
1086             Index = 0xFFFFFFFF;
1087             BaseSetLastNTError(STATUS_NO_MEMORY);
1088         }
1089         else
1090         {
1091             /* Clear the value. */
1092             Teb->TlsExpansionSlots[Index] = 0;
1093             Index += TLS_MINIMUM_AVAILABLE;
1094         }
1095     }
1096     else
1097     {
1098         /* Fail */
1099         BaseSetLastNTError(STATUS_NO_MEMORY);
1100     }
1101 
1102     /* Release the lock and return */
1103     RtlReleasePebLock();
1104     return Index;
1105 }
1106 
1107 /*
1108  * @implemented
1109  */
1110 BOOL
1111 WINAPI
1112 TlsFree(IN DWORD Index)
1113 {
1114     BOOL BitSet;
1115     PPEB Peb;
1116     ULONG TlsIndex;
1117     PVOID TlsBitmap;
1118     NTSTATUS Status;
1119 
1120     /* Acquire the PEB lock and grab the PEB */
1121     Peb = NtCurrentPeb();
1122     RtlAcquirePebLock();
1123 
1124     /* Check if the index is too high */
1125     if (Index >= TLS_MINIMUM_AVAILABLE)
1126     {
1127         /* Check if it can fit in the expansion slots */
1128         TlsIndex = Index - TLS_MINIMUM_AVAILABLE;
1129         if (TlsIndex >= TLS_EXPANSION_SLOTS)
1130         {
1131             /* It's invalid */
1132             BaseSetLastNTError(STATUS_INVALID_PARAMETER);
1133             RtlReleasePebLock();
1134             return FALSE;
1135         }
1136         else
1137         {
1138             /* Use the expansion bitmap */
1139             TlsBitmap = Peb->TlsExpansionBitmap;
1140             Index = TlsIndex;
1141         }
1142     }
1143     else
1144     {
1145         /* Use the normal bitmap */
1146         TlsBitmap = Peb->TlsBitmap;
1147     }
1148 
1149     /* Check if the index was set */
1150     BitSet = RtlAreBitsSet(TlsBitmap, Index, 1);
1151     if (BitSet)
1152     {
1153         /* Tell the kernel to free the TLS cells */
1154         Status = NtSetInformationThread(NtCurrentThread(),
1155                                         ThreadZeroTlsCell,
1156                                         &Index,
1157                                         sizeof(DWORD));
1158         if (!NT_SUCCESS(Status))
1159         {
1160             BaseSetLastNTError(STATUS_INVALID_PARAMETER);
1161             RtlReleasePebLock();
1162             return FALSE;
1163         }
1164 
1165         /* Clear the bit */
1166         RtlClearBits(TlsBitmap, Index, 1);
1167     }
1168     else
1169     {
1170         /* Fail */
1171         BaseSetLastNTError(STATUS_INVALID_PARAMETER);
1172         RtlReleasePebLock();
1173         return FALSE;
1174     }
1175 
1176     /* Done! */
1177     RtlReleasePebLock();
1178     return TRUE;
1179 }
1180 
1181 /*
1182  * @implemented
1183  */
1184 LPVOID
1185 WINAPI
1186 TlsGetValue(IN DWORD Index)
1187 {
1188     PTEB Teb;
1189 
1190     /* Get the TEB and clear the last error */
1191     Teb = NtCurrentTeb();
1192     Teb->LastErrorValue = 0;
1193 
1194     /* Check for simple TLS index */
1195     if (Index < TLS_MINIMUM_AVAILABLE)
1196     {
1197         /* Return it */
1198         return Teb->TlsSlots[Index];
1199     }
1200 
1201     /* Check for valid index */
1202     if (Index >= TLS_EXPANSION_SLOTS + TLS_MINIMUM_AVAILABLE)
1203     {
1204         /* Fail */
1205         BaseSetLastNTError(STATUS_INVALID_PARAMETER);
1206         return NULL;
1207     }
1208 
1209     /* The expansion slots are allocated on demand, so check for it. */
1210     Teb->LastErrorValue = 0;
1211     if (!Teb->TlsExpansionSlots) return NULL;
1212 
1213     /* Return the value from the expansion slots */
1214     return Teb->TlsExpansionSlots[Index - TLS_MINIMUM_AVAILABLE];
1215 }
1216 
1217 /*
1218  * @implemented
1219  */
1220 BOOL
1221 WINAPI
1222 TlsSetValue(IN DWORD Index,
1223             IN LPVOID Value)
1224 {
1225     DWORD TlsIndex;
1226     PTEB Teb = NtCurrentTeb();
1227 
1228     /* Check for simple TLS index */
1229     if (Index < TLS_MINIMUM_AVAILABLE)
1230     {
1231         /* Return it */
1232         Teb->TlsSlots[Index] = Value;
1233         return TRUE;
1234     }
1235 
1236     /* Check if this is an expansion slot */
1237     TlsIndex = Index - TLS_MINIMUM_AVAILABLE;
1238     if (TlsIndex >= TLS_EXPANSION_SLOTS)
1239     {
1240         /* Fail */
1241         BaseSetLastNTError(STATUS_INVALID_PARAMETER);
1242         return FALSE;
1243     }
1244 
1245     /* Do we not have expansion slots? */
1246     if (!Teb->TlsExpansionSlots)
1247     {
1248         /* Get the PEB lock to see if we still need them */
1249         RtlAcquirePebLock();
1250         if (!Teb->TlsExpansionSlots)
1251         {
1252             /* Allocate them */
1253             Teb->TlsExpansionSlots = RtlAllocateHeap(RtlGetProcessHeap(),
1254                                                      HEAP_ZERO_MEMORY,
1255                                                      TLS_EXPANSION_SLOTS *
1256                                                      sizeof(PVOID));
1257             if (!Teb->TlsExpansionSlots)
1258             {
1259                 /* Fail */
1260                 RtlReleasePebLock();
1261                 BaseSetLastNTError(STATUS_NO_MEMORY);
1262                 return FALSE;
1263             }
1264         }
1265 
1266         /* Release the lock */
1267         RtlReleasePebLock();
1268     }
1269 
1270     /* Write the value */
1271     Teb->TlsExpansionSlots[TlsIndex] = Value;
1272 
1273     /* Success */
1274     return TRUE;
1275 }
1276 
1277 /* EOF */
1278