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