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