:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / lib / kernel32 / thread / thread.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS system libraries
5  * FILE:            lib/kernel32/thread/thread.c
6  * PURPOSE:         Thread functions
7  * PROGRAMMER:      Ariadne ( ariadne@xs4all.nl)
8  *                      Tls functions are modified from WINE
9  * UPDATE HISTORY:
10  *                  Created 01/11/98
11  */
12
13 /* INCLUDES ******************************************************************/
14
15 #include <windows.h>
16 #include <kernel32/thread.h>
17 #include <ntdll/ldr.h>
18 #include <string.h>
19 #include <napi/i386/segment.h>
20
21 #define NDEBUG
22 #include <kernel32/kernel32.h>
23 #include <kernel32/error.h>
24
25
26 static VOID ThreadAttachDlls (VOID);
27
28 /* FUNCTIONS *****************************************************************/
29
30 static\r
31 EXCEPTION_DISPOSITION\r
32 __cdecl\r
33 _except_handler(\r
34     struct _EXCEPTION_RECORD *ExceptionRecord,\r
35     void * EstablisherFrame,\r
36     struct _CONTEXT *ContextRecord,\r
37     void * DispatcherContext )\r
38 {\r
39         ExitThread(0);
40
41         /* We should not get to here */
42         return ExceptionContinueSearch;
43 }\r
44
45 static VOID STDCALL
46 ThreadStartup (LPTHREAD_START_ROUTINE lpStartAddress,
47                LPVOID lpParameter)
48 {
49    UINT uExitCode;
50
51    __try1(_except_handler)
52    {
53                 /* FIXME: notify csrss of thread creation ?? */
54                 uExitCode = (lpStartAddress)(lpParameter);
55    } __except1
56    {
57    }
58
59    ExitThread(uExitCode);
60 }
61
62 HANDLE STDCALL CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
63                             DWORD dwStackSize,
64                             LPTHREAD_START_ROUTINE lpStartAddress,
65                             LPVOID lpParameter,
66                             DWORD dwCreationFlags,
67                             LPDWORD lpThreadId)
68 {
69    return(CreateRemoteThread(NtCurrentProcess(),
70                              lpThreadAttributes,
71                              dwStackSize,
72                              lpStartAddress,
73                              lpParameter,
74                              dwCreationFlags,
75                              lpThreadId));
76 }
77
78 HANDLE STDCALL CreateRemoteThread(HANDLE hProcess,
79                                   LPSECURITY_ATTRIBUTES lpThreadAttributes,
80                                   DWORD dwStackSize,
81                                   LPTHREAD_START_ROUTINE lpStartAddress,
82                                   LPVOID lpParameter,
83                                   DWORD dwCreationFlags,
84                                   LPDWORD lpThreadId)
85 {
86    HANDLE ThreadHandle;
87    OBJECT_ATTRIBUTES ObjectAttributes;
88    CLIENT_ID ClientId;
89    CONTEXT ThreadContext;
90    INITIAL_TEB InitialTeb;
91    BOOLEAN CreateSuspended = FALSE;
92    PVOID BaseAddress;
93    ULONG OldPageProtection;
94    NTSTATUS Status;
95    
96    ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
97    ObjectAttributes.RootDirectory = NULL;
98    ObjectAttributes.ObjectName = NULL;
99    ObjectAttributes.Attributes = 0;
100    if (lpThreadAttributes != NULL) 
101      {
102         if (lpThreadAttributes->bInheritHandle)
103           ObjectAttributes.Attributes = OBJ_INHERIT;
104         ObjectAttributes.SecurityDescriptor = 
105           lpThreadAttributes->lpSecurityDescriptor;
106      }
107    ObjectAttributes.SecurityQualityOfService = NULL;
108    
109    if ((dwCreationFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED)
110      CreateSuspended = TRUE;
111    else
112      CreateSuspended = FALSE;
113
114   InitialTeb.StackReserve = 0x100000; /* 1MByte */
115   /* FIXME: use correct commit size */
116 #if 0
117   InitialTeb.StackCommit = (dwStackSize == 0) ? PAGE_SIZE : dwStackSize;
118 #endif
119   InitialTeb.StackCommit = InitialTeb.StackReserve - PAGE_SIZE;
120
121   /* size of guard page */
122   InitialTeb.StackCommit += PAGE_SIZE;
123
124   /* Reserve stack */
125   InitialTeb.StackAllocate = NULL;
126   Status = NtAllocateVirtualMemory(hProcess,
127                                    &InitialTeb.StackAllocate,
128                                    0,
129                                    &InitialTeb.StackReserve,
130                                    MEM_RESERVE,
131                                    PAGE_READWRITE);
132   if (!NT_SUCCESS(Status))
133     {
134       DPRINT("Error reserving stack space!\n");
135       SetLastErrorByStatus(Status);
136       return(NULL);
137     }
138
139   DPRINT("StackDeallocation: %p ReserveSize: 0x%lX\n",
140          InitialTeb.StackDeallocation, InitialTeb.StackReserve);
141
142   InitialTeb.StackBase = (PVOID)((ULONG)InitialTeb.StackAllocate + InitialTeb.StackReserve);
143   InitialTeb.StackLimit = (PVOID)((ULONG)InitialTeb.StackBase - InitialTeb.StackCommit);
144
145   DPRINT("StackBase: %p\nStackCommit: 0x%lX\n",
146          InitialTeb.StackBase,
147          InitialTeb.StackCommit);
148
149   /* Commit stack pages */
150   Status = NtAllocateVirtualMemory(hProcess,
151                                    &InitialTeb.StackLimit,
152                                    0,
153                                    &InitialTeb.StackCommit,
154                                    MEM_COMMIT,
155                                    PAGE_READWRITE);
156   if (!NT_SUCCESS(Status))
157     {
158       /* release the stack space */
159       NtFreeVirtualMemory(hProcess,
160                           InitialTeb.StackAllocate,
161                           &InitialTeb.StackReserve,
162                           MEM_RELEASE);
163
164       DPRINT("Error comitting stack page(s)!\n");
165       SetLastErrorByStatus(Status);
166       return(NULL);
167     }
168
169   DPRINT("StackLimit: %p\n",
170          InitialTeb.StackLimit);
171
172   /* Protect guard page */
173   Status = NtProtectVirtualMemory(hProcess,
174                                   InitialTeb.StackLimit,
175                                   PAGE_SIZE,
176                                   PAGE_GUARD | PAGE_READWRITE,
177                                   &OldPageProtection);
178   if (!NT_SUCCESS(Status))
179     {
180       /* release the stack space */
181       NtFreeVirtualMemory(hProcess,
182                           InitialTeb.StackAllocate,
183                           &InitialTeb.StackReserve,
184                           MEM_RELEASE);
185
186       DPRINT("Error comitting guard page!\n");
187       SetLastErrorByStatus(Status);
188       return(NULL);
189     }
190
191   memset(&ThreadContext,0,sizeof(CONTEXT));
192   ThreadContext.Eip = (LONG)ThreadStartup;
193   ThreadContext.SegGs = USER_DS;
194   ThreadContext.SegFs = TEB_SELECTOR;
195   ThreadContext.SegEs = USER_DS;
196   ThreadContext.SegDs = USER_DS;
197   ThreadContext.SegCs = USER_CS;
198   ThreadContext.SegSs = USER_DS;
199   ThreadContext.Esp = (ULONG)InitialTeb.StackBase - 12;
200   ThreadContext.EFlags = (1<<1) + (1<<9);
201
202   /* initialize call stack */
203   *((PULONG)((ULONG)InitialTeb.StackBase - 4)) = (ULONG)lpParameter;
204   *((PULONG)((ULONG)InitialTeb.StackBase - 8)) = (ULONG)lpStartAddress;
205   *((PULONG)((ULONG)InitialTeb.StackBase - 12)) = 0xdeadbeef;
206
207   DPRINT("Esp: %p\n", ThreadContext.Esp);
208   DPRINT("Eip: %p\n", ThreadContext.Eip);
209
210   Status = NtCreateThread(&ThreadHandle,
211                           THREAD_ALL_ACCESS,
212                           &ObjectAttributes,
213                           hProcess,
214                           &ClientId,
215                           &ThreadContext,
216                           &InitialTeb,
217                           CreateSuspended);
218   if (!NT_SUCCESS(Status))
219     {
220       NtFreeVirtualMemory(hProcess,
221                           InitialTeb.StackAllocate,
222                           &InitialTeb.StackReserve,
223                           MEM_RELEASE);
224
225       DPRINT("NtCreateThread() failed!\n");
226       SetLastErrorByStatus(Status);
227       return(NULL);
228     }
229
230   if (lpThreadId != NULL)
231     memcpy(lpThreadId, &ClientId.UniqueThread,sizeof(ULONG));
232
233   return(ThreadHandle);
234 }
235
236 PTEB
237 GetTeb(VOID)
238 {
239   return(NtCurrentTeb());
240 }
241
242 WINBOOL STDCALL
243 SwitchToThread(VOID)
244 {
245    NTSTATUS errCode;
246    errCode = NtYieldExecution();
247    return TRUE;
248 }
249
250 DWORD STDCALL
251 GetCurrentThreadId()
252 {
253    return((DWORD)(NtCurrentTeb()->Cid).UniqueThread);
254 }
255
256 VOID STDCALL
257 ExitThread(DWORD uExitCode)
258 {
259    NTSTATUS errCode;
260    BOOLEAN LastThread;
261    NTSTATUS Status;
262
263    /*
264     * Terminate process if this is the last thread
265     * of the current process
266     */
267    Status = NtQueryInformationThread(NtCurrentThread(),
268                                      ThreadAmILastThread,
269                                      &LastThread,
270                                      sizeof(BOOLEAN),
271                                      NULL);
272    if (NT_SUCCESS(Status) && LastThread == TRUE)
273      {
274         ExitProcess (uExitCode);
275      }
276
277    /* FIXME: notify csrss of thread termination */
278
279    LdrShutdownThread();
280
281    errCode = NtTerminateThread(NtCurrentThread(),
282                                uExitCode);
283    if (!NT_SUCCESS(errCode))
284      {
285         SetLastErrorByStatus(errCode);
286      }
287 }
288
289 WINBOOL STDCALL GetThreadTimes(HANDLE hThread,
290                                LPFILETIME lpCreationTime,
291                                LPFILETIME lpExitTime,
292                                LPFILETIME lpKernelTime,
293                                LPFILETIME lpUserTime)
294 {
295    NTSTATUS errCode;
296    KERNEL_USER_TIMES KernelUserTimes;
297    ULONG ReturnLength;
298    
299    errCode = NtQueryInformationThread(hThread,
300                                       ThreadTimes,
301                                       &KernelUserTimes,
302                                       sizeof(KERNEL_USER_TIMES),
303                                       &ReturnLength);
304    if (!NT_SUCCESS(errCode))
305      {
306         SetLastErrorByStatus(errCode);
307         return FALSE;
308      }
309    memcpy(lpCreationTime, &KernelUserTimes.CreateTime, sizeof(FILETIME));
310    memcpy(lpExitTime, &KernelUserTimes.ExitTime, sizeof(FILETIME));
311    memcpy(lpKernelTime, &KernelUserTimes.KernelTime, sizeof(FILETIME));
312    memcpy(lpUserTime, &KernelUserTimes.UserTime, sizeof(FILETIME));
313    return TRUE;
314 }
315
316
317 WINBOOL STDCALL GetThreadContext(HANDLE hThread,
318                                  LPCONTEXT lpContext)
319 {
320    NTSTATUS errCode;
321    
322    errCode = NtGetContextThread(hThread,
323                                 lpContext);
324    if (!NT_SUCCESS(errCode))
325      {
326         SetLastErrorByStatus(errCode);
327         return FALSE;
328      }
329    return TRUE;
330 }
331
332 WINBOOL STDCALL SetThreadContext(HANDLE hThread,
333                                  CONST CONTEXT *lpContext)
334 {
335    NTSTATUS errCode;
336    
337    errCode = NtSetContextThread(hThread,
338                                 (void *)lpContext);
339    if (!NT_SUCCESS(errCode))
340      {
341         SetLastErrorByStatus(errCode);
342         return FALSE;
343      }
344    return TRUE;
345 }
346
347 WINBOOL STDCALL GetExitCodeThread(HANDLE hThread,
348                                   LPDWORD lpExitCode)
349 {
350    NTSTATUS errCode;
351    THREAD_BASIC_INFORMATION ThreadBasic;
352    ULONG DataWritten;
353    
354    errCode = NtQueryInformationThread(hThread,
355                                       ThreadBasicInformation,
356                                       &ThreadBasic,
357                                       sizeof(THREAD_BASIC_INFORMATION),
358                                       &DataWritten);
359    if (!NT_SUCCESS(errCode))
360      {
361         SetLastErrorByStatus(errCode);
362         return FALSE;
363      }
364    memcpy(lpExitCode, &ThreadBasic.ExitStatus, sizeof(DWORD));
365    return TRUE;
366 }
367
368 DWORD STDCALL ResumeThread(HANDLE hThread)
369 {
370    NTSTATUS errCode;
371    ULONG PreviousResumeCount;
372    
373    errCode = NtResumeThread(hThread,
374                             &PreviousResumeCount);
375    if (!NT_SUCCESS(errCode))
376      {
377         SetLastErrorByStatus(errCode);
378         return  -1;
379      }
380    return PreviousResumeCount;
381 }
382
383
384 WINBOOL STDCALL
385 TerminateThread (HANDLE hThread,
386                  DWORD  dwExitCode)
387 {
388     if (0 == hThread)
389     {
390         SetLastError (ERROR_INVALID_HANDLE);
391     }
392     else
393     {
394         NTSTATUS Status = NtTerminateThread (hThread, dwExitCode);
395         
396         if (NT_SUCCESS(Status))
397         {
398             return TRUE;
399         }
400         SetLastErrorByStatus (Status);
401     }
402     return FALSE;
403 }
404
405
406 DWORD STDCALL SuspendThread(HANDLE hThread)
407 {
408    NTSTATUS errCode;
409    ULONG PreviousSuspendCount;
410    
411    errCode = NtSuspendThread(hThread,
412                              &PreviousSuspendCount);
413    if (!NT_SUCCESS(errCode))
414      {
415         SetLastErrorByStatus(errCode);
416         return -1;
417      }
418    return PreviousSuspendCount;
419 }
420
421 DWORD STDCALL SetThreadAffinityMask(HANDLE hThread,
422                                     DWORD dwThreadAffinityMask)
423 {
424    return 0;
425 }
426
427 WINBOOL STDCALL SetThreadPriority(HANDLE hThread,
428                                   int nPriority)
429 {
430    NTSTATUS errCode;
431    THREAD_BASIC_INFORMATION ThreadBasic;
432    ULONG DataWritten;
433    
434    errCode = NtQueryInformationThread(hThread,
435                                       ThreadBasicInformation,
436                                       &ThreadBasic,
437                                       sizeof(THREAD_BASIC_INFORMATION),
438                                       &DataWritten);
439    if (!NT_SUCCESS(errCode))
440      {
441         SetLastErrorByStatus(errCode);
442         return FALSE;
443      }
444    ThreadBasic.BasePriority = nPriority;
445    errCode = NtSetInformationThread(hThread,
446                                     ThreadBasicInformation,
447                                     &ThreadBasic,
448                                     sizeof(THREAD_BASIC_INFORMATION));
449    if (!NT_SUCCESS(errCode))
450      {
451         SetLastErrorByStatus(errCode);
452         return FALSE;
453      }
454    return TRUE;
455 }
456
457 int STDCALL GetThreadPriority(HANDLE hThread)
458 {
459    NTSTATUS errCode;
460    THREAD_BASIC_INFORMATION ThreadBasic;
461    ULONG DataWritten;
462    
463    errCode = NtQueryInformationThread(hThread,
464                                       ThreadBasicInformation,
465                                       &ThreadBasic,
466                                       sizeof(THREAD_BASIC_INFORMATION),
467                                       &DataWritten);
468    if (!NT_SUCCESS(errCode))
469      {
470         SetLastErrorByStatus(errCode);
471         return THREAD_PRIORITY_ERROR_RETURN;
472      }
473    return ThreadBasic.BasePriority;
474 }
475
476 /* EOF */