Initialize dwOSVersionInfoSize in GlobalMemoryStatus.
[wine] / dlls / kernel / thread.c
1 /*
2  * Win32 threads
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <fcntl.h>
26 #include <stdarg.h>
27 #include <signal.h>
28 #include <sys/types.h>
29 #ifdef HAVE_SYS_TIMES_H
30 #include <sys/times.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35
36 #include "ntstatus.h"
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winerror.h"
40 #include "winnls.h"
41 #include "module.h"
42 #include "thread.h"
43 #include "wine/winbase16.h"
44 #include "wine/exception.h"
45 #include "wine/library.h"
46 #include "wine/pthread.h"
47 #include "wine/server.h"
48 #include "wine/debug.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(thread);
51 WINE_DECLARE_DEBUG_CHANNEL(relay);
52
53
54 /***********************************************************************
55  *           THREAD_InitStack
56  *
57  * Allocate the stack of a thread.
58  */
59 TEB *THREAD_InitStack( TEB *teb, DWORD stack_size )
60 {
61     DWORD old_prot;
62     DWORD page_size = getpagesize();
63     void *base;
64
65     stack_size = (stack_size + (page_size - 1)) & ~(page_size - 1);
66     if (stack_size < 1024 * 1024) stack_size = 1024 * 1024;  /* Xlib needs a large stack */
67
68     if (!(base = VirtualAlloc( NULL, stack_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE )))
69         return NULL;
70
71     teb->DeallocationStack = base;
72     teb->Tib.StackBase     = (char *)base + stack_size;
73     teb->Tib.StackLimit    = base;  /* note: limit is lower than base since the stack grows down */
74
75     /* Setup guard pages */
76
77     VirtualProtect( base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD, &old_prot );
78     return teb;
79 }
80
81
82 struct new_thread_info
83 {
84     LPTHREAD_START_ROUTINE func;
85     void                  *arg;
86 };
87
88 /***********************************************************************
89  *           THREAD_Start
90  *
91  * Start execution of a newly created thread. Does not return.
92  */
93 static void CALLBACK THREAD_Start( void *ptr )
94 {
95     struct new_thread_info *info = ptr;
96     LPTHREAD_START_ROUTINE func = info->func;
97     void *arg = info->arg;
98
99     RtlFreeHeap( GetProcessHeap(), 0, info );
100
101     if (TRACE_ON(relay))
102         DPRINTF("%04lx:Starting thread (entryproc=%p)\n", GetCurrentThreadId(), func );
103
104     __TRY
105     {
106         MODULE_DllThreadAttach( NULL );
107         ExitThread( func( arg ) );
108     }
109     __EXCEPT(UnhandledExceptionFilter)
110     {
111         TerminateThread( GetCurrentThread(), GetExceptionCode() );
112     }
113     __ENDTRY
114 }
115
116
117 /***********************************************************************
118  *           CreateThread   (KERNEL32.@)
119  */
120 HANDLE WINAPI CreateThread( SECURITY_ATTRIBUTES *sa, SIZE_T stack,
121                             LPTHREAD_START_ROUTINE start, LPVOID param,
122                             DWORD flags, LPDWORD id )
123 {
124      return CreateRemoteThread( GetCurrentProcess(),
125                                 sa, stack, start, param, flags, id );
126 }
127
128
129 /***************************************************************************
130  *                  CreateRemoteThread   (KERNEL32.@)
131  *
132  * Creates a thread that runs in the address space of another process
133  *
134  * PARAMS
135  *
136  * RETURNS
137  *   Success: Handle to the new thread.
138  *   Failure: NULL. Use GetLastError() to find the error cause.
139  *
140  * BUGS
141  *   Improper memory allocation: there's no ability to free new_thread_info
142  *   in other process.
143  *   Bad start address for RtlCreateUserThread because the library
144  *   may be loaded at different address in other process.
145  */
146 HANDLE WINAPI CreateRemoteThread( HANDLE hProcess, SECURITY_ATTRIBUTES *sa, SIZE_T stack,
147                                   LPTHREAD_START_ROUTINE start, LPVOID param,
148                                   DWORD flags, LPDWORD id )
149 {
150     HANDLE handle;
151     CLIENT_ID client_id;
152     NTSTATUS status;
153     SIZE_T stack_reserve = 0, stack_commit = 0;
154     struct new_thread_info *info;
155
156     if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) )))
157     {
158         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
159         return 0;
160     }
161     info->func = start;
162     info->arg  = param;
163
164     if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack;
165     else stack_commit = stack;
166
167     status = RtlCreateUserThread( hProcess, NULL, TRUE,
168                                   NULL, stack_reserve, stack_commit,
169                                   THREAD_Start, info, &handle, &client_id );
170     if (status == STATUS_SUCCESS)
171     {
172         if (id) *id = (DWORD)client_id.UniqueThread;
173         if (sa && (sa->nLength >= sizeof(*sa)) && sa->bInheritHandle)
174             SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT );
175         if (!(flags & CREATE_SUSPENDED))
176         {
177             ULONG ret;
178             if (NtResumeThread( handle, &ret ))
179             {
180                 NtClose( handle );
181                 RtlFreeHeap( GetProcessHeap(), 0, info );
182                 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
183                 handle = 0;
184             }
185         }
186     }
187     else
188     {
189         RtlFreeHeap( GetProcessHeap(), 0, info );
190         SetLastError( RtlNtStatusToDosError(status) );
191         handle = 0;
192     }
193     return handle;
194 }
195
196
197 /***********************************************************************
198  * OpenThread  [KERNEL32.@]   Retrieves a handle to a thread from its thread id
199  */
200 HANDLE WINAPI OpenThread( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId )
201 {
202     HANDLE ret = 0;
203     SERVER_START_REQ( open_thread )
204     {
205         req->tid     = dwThreadId;
206         req->access  = dwDesiredAccess;
207         req->inherit = bInheritHandle;
208         if (!wine_server_call_err( req )) ret = reply->handle;
209     }
210     SERVER_END_REQ;
211     return ret;
212 }
213
214
215 /***********************************************************************
216  * ExitThread [KERNEL32.@]  Ends a thread
217  *
218  * RETURNS
219  *    None
220  */
221 void WINAPI ExitThread( DWORD code ) /* [in] Exit code for this thread */
222 {
223     BOOL last;
224     SERVER_START_REQ( terminate_thread )
225     {
226         /* send the exit code to the server */
227         req->handle    = GetCurrentThread();
228         req->exit_code = code;
229         wine_server_call( req );
230         last = reply->last;
231     }
232     SERVER_END_REQ;
233
234     if (last)
235     {
236         LdrShutdownProcess();
237         exit( code );
238     }
239     else
240     {
241         struct wine_pthread_thread_info info;
242         sigset_t block_set;
243         ULONG size;
244
245         LdrShutdownThread();
246         RtlAcquirePebLock();
247         RemoveEntryList( &NtCurrentTeb()->TlsLinks );
248         RtlReleasePebLock();
249
250         info.stack_base  = NtCurrentTeb()->DeallocationStack;
251         info.teb_base    = NtCurrentTeb();
252         info.teb_sel     = wine_get_fs();
253         info.exit_status = code;
254
255         size = 0;
256         NtFreeVirtualMemory( GetCurrentProcess(), &info.stack_base, &size, MEM_RELEASE | MEM_SYSTEM );
257         info.stack_size = size;
258
259         size = 0;
260         NtFreeVirtualMemory( GetCurrentProcess(), &info.teb_base, &size, MEM_RELEASE | MEM_SYSTEM );
261         info.teb_size = size;
262
263         /* block the async signals */
264         sigemptyset( &block_set );
265         sigaddset( &block_set, SIGALRM );
266         sigaddset( &block_set, SIGIO );
267         sigaddset( &block_set, SIGINT );
268         sigaddset( &block_set, SIGHUP );
269         sigaddset( &block_set, SIGUSR1 );
270         sigaddset( &block_set, SIGUSR2 );
271         sigaddset( &block_set, SIGTERM );
272         sigprocmask( SIG_BLOCK, &block_set, NULL );
273
274         close( NtCurrentTeb()->wait_fd[0] );
275         close( NtCurrentTeb()->wait_fd[1] );
276         close( NtCurrentTeb()->reply_fd );
277         close( NtCurrentTeb()->request_fd );
278
279         wine_pthread_exit_thread( &info );
280     }
281 }
282
283
284 /**********************************************************************
285  * TerminateThread [KERNEL32.@]  Terminates a thread
286  *
287  * RETURNS
288  *    Success: TRUE
289  *    Failure: FALSE
290  */
291 BOOL WINAPI TerminateThread( HANDLE handle,    /* [in] Handle to thread */
292                              DWORD exit_code)  /* [in] Exit code for thread */
293 {
294     NTSTATUS status = NtTerminateThread( handle, exit_code );
295     if (status) SetLastError( RtlNtStatusToDosError(status) );
296     return !status;
297 }
298
299
300 /***********************************************************************
301  *           FreeLibraryAndExitThread (KERNEL32.@)
302  */
303 void WINAPI FreeLibraryAndExitThread(HINSTANCE hLibModule, DWORD dwExitCode)
304 {
305     FreeLibrary(hLibModule);
306     ExitThread(dwExitCode);
307 }
308
309
310 /**********************************************************************
311  *              GetExitCodeThread (KERNEL32.@)
312  *
313  * Gets termination status of thread.
314  *
315  * RETURNS
316  *    Success: TRUE
317  *    Failure: FALSE
318  */
319 BOOL WINAPI GetExitCodeThread(
320     HANDLE hthread, /* [in]  Handle to thread */
321     LPDWORD exitcode) /* [out] Address to receive termination status */
322 {
323     THREAD_BASIC_INFORMATION info;
324     NTSTATUS status = NtQueryInformationThread( hthread, ThreadBasicInformation,
325                                                 &info, sizeof(info), NULL );
326
327     if (status)
328     {
329         SetLastError( RtlNtStatusToDosError(status) );
330         return FALSE;
331     }
332     if (exitcode) *exitcode = info.ExitStatus;
333     return TRUE;
334 }
335
336
337 /***********************************************************************
338  * SetThreadContext [KERNEL32.@]  Sets context of thread.
339  *
340  * RETURNS
341  *    Success: TRUE
342  *    Failure: FALSE
343  */
344 BOOL WINAPI SetThreadContext( HANDLE handle,           /* [in]  Handle to thread with context */
345                               const CONTEXT *context ) /* [in] Address of context structure */
346 {
347     NTSTATUS status = NtSetContextThread( handle, context );
348     if (status) SetLastError( RtlNtStatusToDosError(status) );
349     return !status;
350 }
351
352
353 /***********************************************************************
354  * GetThreadContext [KERNEL32.@]  Retrieves context of thread.
355  *
356  * RETURNS
357  *    Success: TRUE
358  *    Failure: FALSE
359  */
360 BOOL WINAPI GetThreadContext( HANDLE handle,     /* [in]  Handle to thread with context */
361                               CONTEXT *context ) /* [out] Address of context structure */
362 {
363     NTSTATUS status = NtGetContextThread( handle, context );
364     if (status) SetLastError( RtlNtStatusToDosError(status) );
365     return !status;
366 }
367
368
369 /**********************************************************************
370  * SuspendThread [KERNEL32.@]  Suspends a thread.
371  *
372  * RETURNS
373  *    Success: Previous suspend count
374  *    Failure: 0xFFFFFFFF
375  */
376 DWORD WINAPI SuspendThread( HANDLE hthread ) /* [in] Handle to the thread */
377 {
378     DWORD ret;
379     NTSTATUS status = NtSuspendThread( hthread, &ret );
380
381     if (status)
382     {
383         ret = ~0U;
384         SetLastError( RtlNtStatusToDosError(status) );
385     }
386     return ret;
387 }
388
389
390 /**********************************************************************
391  * ResumeThread [KERNEL32.@]  Resumes a thread.
392  *
393  * Decrements a thread's suspend count.  When count is zero, the
394  * execution of the thread is resumed.
395  *
396  * RETURNS
397  *    Success: Previous suspend count
398  *    Failure: 0xFFFFFFFF
399  *    Already running: 0
400  */
401 DWORD WINAPI ResumeThread( HANDLE hthread ) /* [in] Identifies thread to restart */
402 {
403     DWORD ret;
404     NTSTATUS status = NtResumeThread( hthread, &ret );
405
406     if (status)
407     {
408         ret = ~0U;
409         SetLastError( RtlNtStatusToDosError(status) );
410     }
411     return ret;
412 }
413
414
415 /**********************************************************************
416  * GetThreadPriority [KERNEL32.@]  Returns priority for thread.
417  *
418  * RETURNS
419  *    Success: Thread's priority level.
420  *    Failure: THREAD_PRIORITY_ERROR_RETURN
421  */
422 INT WINAPI GetThreadPriority(
423     HANDLE hthread) /* [in] Handle to thread */
424 {
425     THREAD_BASIC_INFORMATION info;
426     NTSTATUS status = NtQueryInformationThread( hthread, ThreadBasicInformation,
427                                                 &info, sizeof(info), NULL );
428
429     if (status)
430     {
431         SetLastError( RtlNtStatusToDosError(status) );
432         return THREAD_PRIORITY_ERROR_RETURN;
433     }
434     return info.Priority;
435 }
436
437
438 /**********************************************************************
439  * SetThreadPriority [KERNEL32.@]  Sets priority for thread.
440  *
441  * RETURNS
442  *    Success: TRUE
443  *    Failure: FALSE
444  */
445 BOOL WINAPI SetThreadPriority(
446     HANDLE hthread, /* [in] Handle to thread */
447     INT priority)   /* [in] Thread priority level */
448 {
449     BOOL ret;
450     SERVER_START_REQ( set_thread_info )
451     {
452         req->handle   = hthread;
453         req->priority = priority;
454         req->mask     = SET_THREAD_INFO_PRIORITY;
455         ret = !wine_server_call_err( req );
456     }
457     SERVER_END_REQ;
458     return ret;
459 }
460
461
462 /**********************************************************************
463  * GetThreadPriorityBoost [KERNEL32.@]  Returns priority boost for thread.
464  *
465  * Always reports that priority boost is disabled.
466  *
467  * RETURNS
468  *    Success: TRUE.
469  *    Failure: FALSE
470  */
471 BOOL WINAPI GetThreadPriorityBoost(
472     HANDLE hthread, /* [in] Handle to thread */
473     PBOOL pstate)   /* [out] pointer to var that receives the boost state */
474 {
475     if (pstate) *pstate = FALSE;
476     return NO_ERROR;
477 }
478
479
480 /**********************************************************************
481  * SetThreadPriorityBoost [KERNEL32.@]  Sets priority boost for thread.
482  *
483  * Priority boost is not implemented. Thsi function always returns
484  * FALSE and sets last error to ERROR_CALL_NOT_IMPLEMENTED
485  *
486  * RETURNS
487  *    Always returns FALSE to indicate a failure
488  */
489 BOOL WINAPI SetThreadPriorityBoost(
490     HANDLE hthread, /* [in] Handle to thread */
491     BOOL disable)   /* [in] TRUE to disable priority boost */
492 {
493     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
494     return FALSE;
495 }
496
497
498 /**********************************************************************
499  *           SetThreadAffinityMask   (KERNEL32.@)
500  */
501 DWORD WINAPI SetThreadAffinityMask( HANDLE hThread, DWORD dwThreadAffinityMask )
502 {
503     DWORD ret;
504     SERVER_START_REQ( set_thread_info )
505     {
506         req->handle   = hThread;
507         req->affinity = dwThreadAffinityMask;
508         req->mask     = SET_THREAD_INFO_AFFINITY;
509         ret = !wine_server_call_err( req );
510         /* FIXME: should return previous value */
511     }
512     SERVER_END_REQ;
513     return ret;
514 }
515
516
517 /**********************************************************************
518  * SetThreadIdealProcessor [KERNEL32.@]  Obtains timing information.
519  *
520  * RETURNS
521  *    Success: Value of last call to SetThreadIdealProcessor
522  *    Failure: -1
523  */
524 DWORD WINAPI SetThreadIdealProcessor(
525     HANDLE hThread,          /* [in] Specifies the thread of interest */
526     DWORD dwIdealProcessor)  /* [in] Specifies the new preferred processor */
527 {
528     FIXME("(%p): stub\n",hThread);
529     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
530     return -1L;
531 }
532
533
534 /* callback for QueueUserAPC */
535 static void CALLBACK call_user_apc( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 )
536 {
537     PAPCFUNC func = (PAPCFUNC)arg1;
538     func( arg2 );
539 }
540
541 /***********************************************************************
542  *              QueueUserAPC  (KERNEL32.@)
543  */
544 DWORD WINAPI QueueUserAPC( PAPCFUNC func, HANDLE hthread, ULONG_PTR data )
545 {
546     NTSTATUS status = NtQueueApcThread( hthread, call_user_apc, (ULONG_PTR)func, data, 0 );
547
548     if (status) SetLastError( RtlNtStatusToDosError(status) );
549     return !status;
550 }
551
552
553 /**********************************************************************
554  * GetThreadTimes [KERNEL32.@]  Obtains timing information.
555  *
556  * RETURNS
557  *    Success: TRUE
558  *    Failure: FALSE
559  */
560 BOOL WINAPI GetThreadTimes(
561     HANDLE thread,         /* [in]  Specifies the thread of interest */
562     LPFILETIME creationtime, /* [out] When the thread was created */
563     LPFILETIME exittime,     /* [out] When the thread was destroyed */
564     LPFILETIME kerneltime,   /* [out] Time thread spent in kernel mode */
565     LPFILETIME usertime)     /* [out] Time thread spent in user mode */
566 {
567     BOOL ret = TRUE;
568
569     if (creationtime || exittime)
570     {
571         /* We need to do a server call to get the creation time or exit time */
572         /* This works on any thread */
573
574         SERVER_START_REQ( get_thread_info )
575         {
576             req->handle = thread;
577             req->tid_in = 0;
578             if ((ret = !wine_server_call_err( req )))
579             {
580                 if (creationtime)
581                     RtlSecondsSince1970ToTime( reply->creation_time, (LARGE_INTEGER*)creationtime );
582                 if (exittime)
583                     RtlSecondsSince1970ToTime( reply->exit_time, (LARGE_INTEGER*)exittime );
584             }
585         }
586         SERVER_END_REQ;
587     }
588     if (ret && (kerneltime || usertime))
589     {
590         /* We call times(2) for kernel time or user time */
591         /* We can only (portably) do this for the current thread */
592         if (thread == GetCurrentThread())
593         {
594             ULONGLONG time;
595             struct tms time_buf;
596             long clocks_per_sec = sysconf(_SC_CLK_TCK);
597
598             times(&time_buf);
599             if (kerneltime)
600             {
601                 time = (ULONGLONG)time_buf.tms_stime * 10000000 / clocks_per_sec;
602                 kerneltime->dwHighDateTime = time >> 32;
603                 kerneltime->dwLowDateTime = (DWORD)time;
604             }
605             if (usertime)
606             {
607                 time = (ULONGLONG)time_buf.tms_utime * 10000000 / clocks_per_sec;
608                 usertime->dwHighDateTime = time >> 32;
609                 usertime->dwLowDateTime = (DWORD)time;
610             }
611         }
612         else
613         {
614             if (kerneltime) kerneltime->dwHighDateTime = kerneltime->dwLowDateTime = 0;
615             if (usertime) usertime->dwHighDateTime = usertime->dwLowDateTime = 0;
616             FIXME("Cannot get kerneltime or usertime of other threads\n");
617         }
618     }
619     return ret;
620 }
621
622
623 /**********************************************************************
624  * VWin32_BoostThreadGroup [KERNEL.535]
625  */
626 VOID WINAPI VWin32_BoostThreadGroup( DWORD threadId, INT boost )
627 {
628     FIXME("(0x%08lx,%d): stub\n", threadId, boost);
629 }
630
631
632 /**********************************************************************
633  * VWin32_BoostThreadStatic [KERNEL.536]
634  */
635 VOID WINAPI VWin32_BoostThreadStatic( DWORD threadId, INT boost )
636 {
637     FIXME("(0x%08lx,%d): stub\n", threadId, boost);
638 }
639
640
641 /***********************************************************************
642  * GetCurrentThread [KERNEL32.@]  Gets pseudohandle for current thread
643  *
644  * RETURNS
645  *    Pseudohandle for the current thread
646  */
647 #undef GetCurrentThread
648 HANDLE WINAPI GetCurrentThread(void)
649 {
650     return (HANDLE)0xfffffffe;
651 }
652
653
654 #ifdef __i386__
655
656 /***********************************************************************
657  *              SetLastError (KERNEL.147)
658  *              SetLastError (KERNEL32.@)
659  */
660 /* void WINAPI SetLastError( DWORD error ); */
661 __ASM_GLOBAL_FUNC( SetLastError,
662                    "movl 4(%esp),%eax\n\t"
663                    ".byte 0x64\n\t"
664                    "movl %eax,0x34\n\t"
665                    "ret $4" )
666
667 /***********************************************************************
668  *              GetLastError (KERNEL.148)
669  *              GetLastError (KERNEL32.@)
670  */
671 /* DWORD WINAPI GetLastError(void); */
672 __ASM_GLOBAL_FUNC( GetLastError, ".byte 0x64\n\tmovl 0x34,%eax\n\tret" )
673
674 /***********************************************************************
675  *              GetCurrentProcessId (KERNEL.471)
676  *              GetCurrentProcessId (KERNEL32.@)
677  */
678 /* DWORD WINAPI GetCurrentProcessId(void) */
679 __ASM_GLOBAL_FUNC( GetCurrentProcessId, ".byte 0x64\n\tmovl 0x20,%eax\n\tret" )
680
681 /***********************************************************************
682  *              GetCurrentThreadId (KERNEL.462)
683  *              GetCurrentThreadId (KERNEL32.@)
684  */
685 /* DWORD WINAPI GetCurrentThreadId(void) */
686 __ASM_GLOBAL_FUNC( GetCurrentThreadId, ".byte 0x64\n\tmovl 0x24,%eax\n\tret" )
687
688 #else  /* __i386__ */
689
690 /**********************************************************************
691  *              SetLastError (KERNEL.147)
692  *              SetLastError (KERNEL32.@)
693  *
694  * Sets the last-error code.
695  */
696 void WINAPI SetLastError( DWORD error ) /* [in] Per-thread error code */
697 {
698     NtCurrentTeb()->LastErrorValue = error;
699 }
700
701 /**********************************************************************
702  *              GetLastError (KERNEL.148)
703  *              GetLastError (KERNEL32.@)
704  *
705  * Returns last-error code.
706  */
707 DWORD WINAPI GetLastError(void)
708 {
709     return NtCurrentTeb()->LastErrorValue;
710 }
711
712 /***********************************************************************
713  *              GetCurrentProcessId (KERNEL.471)
714  *              GetCurrentProcessId (KERNEL32.@)
715  *
716  * Returns process identifier.
717  */
718 DWORD WINAPI GetCurrentProcessId(void)
719 {
720     return (DWORD)NtCurrentTeb()->ClientId.UniqueProcess;
721 }
722
723 /***********************************************************************
724  *              GetCurrentThreadId (KERNEL.462)
725  *              GetCurrentThreadId (KERNEL32.@)
726  *
727  * Returns thread identifier.
728  */
729 DWORD WINAPI GetCurrentThreadId(void)
730 {
731     return (DWORD)NtCurrentTeb()->ClientId.UniqueThread;
732 }
733
734 #endif  /* __i386__ */