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