xref: /reactos/dll/win32/kernel32/client/thread.c (revision 75e2fb45)
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
BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,IN PCLIENT_ID ClientId)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
BaseThreadStartup(_In_ LPTHREAD_START_ROUTINE lpStartAddress,_In_ LPVOID lpParameter)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
BaseDispatchApc(IN PAPCFUNC ApcRoutine,IN PVOID Data,IN PACTIVATION_CONTEXT ActivationContext)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
CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes,IN DWORD dwStackSize,IN LPTHREAD_START_ROUTINE lpStartAddress,IN LPVOID lpParameter,IN DWORD dwCreationFlags,OUT LPDWORD lpThreadId)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
CreateRemoteThread(IN HANDLE hProcess,IN LPSECURITY_ATTRIBUTES lpThreadAttributes,IN DWORD dwStackSize,IN LPTHREAD_START_ROUTINE lpStartAddress,IN LPVOID lpParameter,IN DWORD dwCreationFlags,OUT LPDWORD lpThreadId)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
ExitThread(IN DWORD uExitCode)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
OpenThread(IN DWORD dwDesiredAccess,IN BOOL bInheritHandle,IN DWORD dwThreadId)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
GetTeb(VOID)438 GetTeb(VOID)
439 {
440     return NtCurrentTeb();
441 }
442 
443 /*
444  * @implemented
445  */
446 BOOL
447 WINAPI
SwitchToThread(VOID)448 SwitchToThread(VOID)
449 {
450     return NtYieldExecution() != STATUS_NO_YIELD_PERFORMED;
451 }
452 
453 
454 /*
455  * @implemented
456  */
457 DWORD
458 WINAPI
GetCurrentThreadId(VOID)459 GetCurrentThreadId(VOID)
460 {
461     return HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
462 }
463 
464 /*
465  * @implemented
466  */
467 BOOL
468 NTAPI
GetThreadTimes(IN HANDLE hThread,OUT LPFILETIME lpCreationTime,OUT LPFILETIME lpExitTime,OUT LPFILETIME lpKernelTime,OUT LPFILETIME lpUserTime)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
GetThreadContext(IN HANDLE hThread,OUT LPCONTEXT lpContext)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
SetThreadContext(IN HANDLE hThread,IN CONST CONTEXT * lpContext)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
GetExitCodeThread(IN HANDLE hThread,OUT LPDWORD lpExitCode)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
ResumeThread(IN HANDLE hThread)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
TerminateThread(IN HANDLE hThread,IN DWORD dwExitCode)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
SuspendThread(IN HANDLE hThread)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
SetThreadAffinityMask(IN HANDLE hThread,IN DWORD_PTR dwThreadAffinityMask)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
SetThreadPriority(IN HANDLE hThread,IN int nPriority)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
GetThreadPriority(IN HANDLE hThread)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
GetThreadPriorityBoost(IN HANDLE hThread,OUT PBOOL pDisablePriorityBoost)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
SetThreadPriorityBoost(IN HANDLE hThread,IN BOOL bDisablePriorityBoost)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
GetThreadSelectorEntry(IN HANDLE hThread,IN DWORD dwSelector,OUT LPLDT_ENTRY lpSelectorEntry)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
SetThreadIdealProcessor(IN HANDLE hThread,IN DWORD dwIdealProcessor)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
GetProcessIdOfThread(IN HANDLE Thread)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
GetThreadId(IN HANDLE Thread)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
SetThreadUILanguage(IN LANGID LangId)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
QueueUserAPC(IN PAPCFUNC pfnAPC,IN HANDLE hThread,IN ULONG_PTR dwData)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
SetThreadStackGuarantee(IN OUT PULONG StackSizeInBytes)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
GetThreadIOPendingFlag(IN HANDLE hThread,OUT PBOOL lpIOIsPending)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
QueueUserWorkItem(IN LPTHREAD_START_ROUTINE Function,IN PVOID Context,IN ULONG Flags)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
TlsAlloc(VOID)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
TlsFree(IN DWORD Index)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
TlsGetValue(IN DWORD Index)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
TlsSetValue(IN DWORD Index,IN LPVOID Value)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