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