4 * Copyright 1996 Alexandre Julliard
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.
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.
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
22 #include "wine/port.h"
27 #include <sys/types.h>
28 #ifdef HAVE_SYS_TIMES_H
29 #include <sys/times.h>
42 #include "wine/winbase16.h"
43 #include "wine/library.h"
44 #include "wine/server.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(thread
);
48 WINE_DECLARE_DEBUG_CHANNEL(relay
);
51 /* TEB of the initial thread */
52 static TEB initial_teb
;
53 extern struct _PDB current_process
;
56 /***********************************************************************
59 * Initialization of a newly created TEB.
61 static BOOL
THREAD_InitTEB( TEB
*teb
)
63 teb
->Tib
.ExceptionList
= (void *)~0UL;
64 teb
->Tib
.StackBase
= (void *)~0UL;
65 teb
->Tib
.Self
= &teb
->Tib
;
66 teb
->tibflags
= TEBF_WIN32
;
67 teb
->exit_code
= STILL_ACTIVE
;
72 teb
->StaticUnicodeString
.MaximumLength
= sizeof(teb
->StaticUnicodeBuffer
);
73 teb
->StaticUnicodeString
.Buffer
= (PWSTR
)teb
->StaticUnicodeBuffer
;
74 InitializeListHead(&teb
->TlsLinks
);
75 teb
->teb_sel
= wine_ldt_alloc_fs();
76 return (teb
->teb_sel
!= 0);
80 /***********************************************************************
83 * Allocate the stack of a thread.
85 TEB
*THREAD_InitStack( TEB
*teb
, DWORD stack_size
)
87 DWORD old_prot
, total_size
;
88 DWORD page_size
= getpagesize();
91 /* Allocate the stack */
93 /* if size is smaller than default, get stack size from parent */
94 if (stack_size
< 1024 * 1024)
97 stack_size
= 1024 * 1024; /* no parent */
99 stack_size
= ((char *)NtCurrentTeb()->Tib
.StackBase
100 - (char *)NtCurrentTeb()->DeallocationStack
101 - SIGNAL_STACK_SIZE
- 3 * page_size
);
104 /* FIXME: some Wine functions use a lot of stack, so we add 64Kb here */
105 stack_size
+= 64 * 1024;
107 /* Memory layout in allocated block:
110 * 1 page NOACCESS guard page
111 * SIGNAL_STACK_SIZE signal stack
112 * 1 page NOACCESS guard page
113 * 1 page PAGE_GUARD guard page
114 * stack_size normal stack
115 * 1 page TEB (except for initial thread)
118 stack_size
= (stack_size
+ (page_size
- 1)) & ~(page_size
- 1);
119 total_size
= stack_size
+ SIGNAL_STACK_SIZE
+ 3 * page_size
;
120 if (!teb
) total_size
+= page_size
;
122 if (!(base
= VirtualAlloc( NULL
, total_size
, MEM_COMMIT
, PAGE_EXECUTE_READWRITE
)))
127 teb
= (TEB
*)((char *)base
+ total_size
- page_size
);
128 if (!THREAD_InitTEB( teb
))
130 VirtualFree( base
, 0, MEM_RELEASE
);
135 teb
->DeallocationStack
= base
;
136 teb
->signal_stack
= (char *)base
+ page_size
;
137 teb
->Tib
.StackBase
= (char *)base
+ 3 * page_size
+ SIGNAL_STACK_SIZE
+ stack_size
;
138 teb
->Tib
.StackLimit
= base
; /* note: limit is lower than base since the stack grows down */
140 /* Setup guard pages */
142 VirtualProtect( base
, 1, PAGE_NOACCESS
, &old_prot
);
143 VirtualProtect( (char *)teb
->signal_stack
+ SIGNAL_STACK_SIZE
, 1, PAGE_NOACCESS
, &old_prot
);
144 VirtualProtect( (char *)teb
->signal_stack
+ SIGNAL_STACK_SIZE
+ page_size
, 1,
145 PAGE_EXECUTE_READWRITE
| PAGE_GUARD
, &old_prot
);
150 /***********************************************************************
153 * Setup the initial thread.
155 * NOTES: The first allocated TEB on NT is at 0x7ffde000.
157 void THREAD_Init(void)
159 static struct debug_info info
; /* debug info for initial thread */
161 if (!initial_teb
.Tib
.Self
) /* do it only once */
163 THREAD_InitTEB( &initial_teb
);
164 assert( initial_teb
.teb_sel
);
165 info
.str_pos
= info
.strings
;
166 info
.out_pos
= info
.output
;
167 initial_teb
.debug_info
= &info
;
168 initial_teb
.Peb
= (PEB
*)¤t_process
; /* FIXME */
169 SYSDEPS_SetCurThread( &initial_teb
);
173 DECL_GLOBAL_CONSTRUCTOR(thread_init
) { THREAD_Init(); }
176 /***********************************************************************
179 * Start execution of a newly created thread. Does not return.
181 static void THREAD_Start( TEB
*teb
)
183 LPTHREAD_START_ROUTINE func
= (LPTHREAD_START_ROUTINE
)teb
->entry_point
;
184 struct debug_info info
;
186 info
.str_pos
= info
.strings
;
187 info
.out_pos
= info
.output
;
188 teb
->debug_info
= &info
;
190 SYSDEPS_SetCurThread( teb
);
192 wine_server_init_thread();
195 DPRINTF("%04lx:Starting thread (entryproc=%p)\n", GetCurrentThreadId(), func
);
199 MODULE_DllThreadAttach( NULL
);
200 ExitThread( func( NtCurrentTeb()->entry_arg
) );
202 __EXCEPT(UnhandledExceptionFilter
)
204 TerminateThread( GetCurrentThread(), GetExceptionCode() );
210 /***********************************************************************
211 * CreateThread (KERNEL32.@)
213 HANDLE WINAPI
CreateThread( SECURITY_ATTRIBUTES
*sa
, SIZE_T stack
,
214 LPTHREAD_START_ROUTINE start
, LPVOID param
,
215 DWORD flags
, LPDWORD id
)
222 if (pipe( request_pipe
) == -1)
224 SetLastError( ERROR_TOO_MANY_OPEN_FILES
);
227 fcntl( request_pipe
[1], F_SETFD
, 1 ); /* set close on exec flag */
228 wine_server_send_fd( request_pipe
[0] );
230 SERVER_START_REQ( new_thread
)
232 req
->suspend
= ((flags
& CREATE_SUSPENDED
) != 0);
233 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
234 req
->request_fd
= request_pipe
[0];
235 if (!wine_server_call_err( req
))
237 handle
= reply
->handle
;
240 close( request_pipe
[0] );
244 if (!handle
|| !(teb
= THREAD_InitStack( NULL
, stack
)))
246 close( request_pipe
[1] );
250 teb
->Peb
= NtCurrentTeb()->Peb
;
251 teb
->ClientId
.UniqueThread
= (HANDLE
)tid
;
252 teb
->request_fd
= request_pipe
[1];
253 teb
->entry_point
= start
;
254 teb
->entry_arg
= param
;
255 teb
->htask16
= GetCurrentTask();
257 InsertHeadList( &NtCurrentTeb()->TlsLinks
, &teb
->TlsLinks
);
261 if (SYSDEPS_SpawnThread( THREAD_Start
, teb
) == -1)
263 CloseHandle( handle
);
264 close( request_pipe
[1] );
266 RemoveEntryList( &teb
->TlsLinks
);
268 wine_ldt_free_fs( teb
->teb_sel
);
269 VirtualFree( teb
->DeallocationStack
, 0, MEM_RELEASE
);
276 /***********************************************************************
277 * OpenThread [KERNEL32.@] Retrieves a handle to a thread from its thread id
279 HANDLE WINAPI
OpenThread( DWORD dwDesiredAccess
, BOOL bInheritHandle
, DWORD dwThreadId
)
282 SERVER_START_REQ( open_thread
)
284 req
->tid
= dwThreadId
;
285 req
->access
= dwDesiredAccess
;
286 req
->inherit
= bInheritHandle
;
287 if (!wine_server_call_err( req
)) ret
= reply
->handle
;
294 /***********************************************************************
295 * ExitThread [KERNEL32.@] Ends a thread
300 void WINAPI
ExitThread( DWORD code
) /* [in] Exit code for this thread */
303 SERVER_START_REQ( terminate_thread
)
305 /* send the exit code to the server */
306 req
->handle
= GetCurrentThread();
307 req
->exit_code
= code
;
308 wine_server_call( req
);
315 LdrShutdownProcess();
322 RemoveEntryList( &NtCurrentTeb()->TlsLinks
);
324 SYSDEPS_ExitThread( code
);
329 /**********************************************************************
330 * TerminateThread [KERNEL32.@] Terminates a thread
336 BOOL WINAPI
TerminateThread( HANDLE handle
, /* [in] Handle to thread */
337 DWORD exit_code
) /* [in] Exit code for thread */
339 NTSTATUS status
= NtTerminateThread( handle
, exit_code
);
340 if (status
) SetLastError( RtlNtStatusToDosError(status
) );
345 /***********************************************************************
346 * FreeLibraryAndExitThread (KERNEL32.@)
348 void WINAPI
FreeLibraryAndExitThread(HINSTANCE hLibModule
, DWORD dwExitCode
)
350 FreeLibrary(hLibModule
);
351 ExitThread(dwExitCode
);
355 /**********************************************************************
356 * GetExitCodeThread (KERNEL32.@)
358 * Gets termination status of thread.
364 BOOL WINAPI
GetExitCodeThread(
365 HANDLE hthread
, /* [in] Handle to thread */
366 LPDWORD exitcode
) /* [out] Address to receive termination status */
368 THREAD_BASIC_INFORMATION info
;
369 NTSTATUS status
= NtQueryInformationThread( hthread
, ThreadBasicInformation
,
370 &info
, sizeof(info
), NULL
);
374 SetLastError( RtlNtStatusToDosError(status
) );
377 if (exitcode
) *exitcode
= info
.ExitStatus
;
382 /***********************************************************************
383 * SetThreadContext [KERNEL32.@] Sets context of thread.
389 BOOL WINAPI
SetThreadContext( HANDLE handle
, /* [in] Handle to thread with context */
390 const CONTEXT
*context
) /* [in] Address of context structure */
392 NTSTATUS status
= NtSetContextThread( handle
, context
);
393 if (status
) SetLastError( RtlNtStatusToDosError(status
) );
398 /***********************************************************************
399 * GetThreadContext [KERNEL32.@] Retrieves context of thread.
405 BOOL WINAPI
GetThreadContext( HANDLE handle
, /* [in] Handle to thread with context */
406 CONTEXT
*context
) /* [out] Address of context structure */
408 NTSTATUS status
= NtGetContextThread( handle
, context
);
409 if (status
) SetLastError( RtlNtStatusToDosError(status
) );
414 /**********************************************************************
415 * SuspendThread [KERNEL32.@] Suspends a thread.
418 * Success: Previous suspend count
419 * Failure: 0xFFFFFFFF
421 DWORD WINAPI
SuspendThread( HANDLE hthread
) /* [in] Handle to the thread */
424 NTSTATUS status
= NtSuspendThread( hthread
, &ret
);
429 SetLastError( RtlNtStatusToDosError(status
) );
435 /**********************************************************************
436 * ResumeThread [KERNEL32.@] Resumes a thread.
438 * Decrements a thread's suspend count. When count is zero, the
439 * execution of the thread is resumed.
442 * Success: Previous suspend count
443 * Failure: 0xFFFFFFFF
446 DWORD WINAPI
ResumeThread( HANDLE hthread
) /* [in] Identifies thread to restart */
449 NTSTATUS status
= NtResumeThread( hthread
, &ret
);
454 SetLastError( RtlNtStatusToDosError(status
) );
460 /**********************************************************************
461 * GetThreadPriority [KERNEL32.@] Returns priority for thread.
464 * Success: Thread's priority level.
465 * Failure: THREAD_PRIORITY_ERROR_RETURN
467 INT WINAPI
GetThreadPriority(
468 HANDLE hthread
) /* [in] Handle to thread */
470 THREAD_BASIC_INFORMATION info
;
471 NTSTATUS status
= NtQueryInformationThread( hthread
, ThreadBasicInformation
,
472 &info
, sizeof(info
), NULL
);
476 SetLastError( RtlNtStatusToDosError(status
) );
477 return THREAD_PRIORITY_ERROR_RETURN
;
479 return info
.Priority
;
483 /**********************************************************************
484 * SetThreadPriority [KERNEL32.@] Sets priority for thread.
490 BOOL WINAPI
SetThreadPriority(
491 HANDLE hthread
, /* [in] Handle to thread */
492 INT priority
) /* [in] Thread priority level */
495 SERVER_START_REQ( set_thread_info
)
497 req
->handle
= hthread
;
498 req
->priority
= priority
;
499 req
->mask
= SET_THREAD_INFO_PRIORITY
;
500 ret
= !wine_server_call_err( req
);
507 /**********************************************************************
508 * GetThreadPriorityBoost [KERNEL32.@] Returns priority boost for thread.
510 * Always reports that priority boost is disabled.
516 BOOL WINAPI
GetThreadPriorityBoost(
517 HANDLE hthread
, /* [in] Handle to thread */
518 PBOOL pstate
) /* [out] pointer to var that receives the boost state */
520 if (pstate
) *pstate
= FALSE
;
525 /**********************************************************************
526 * SetThreadPriorityBoost [KERNEL32.@] Sets priority boost for thread.
528 * Priority boost is not implemented. Thsi function always returns
529 * FALSE and sets last error to ERROR_CALL_NOT_IMPLEMENTED
532 * Always returns FALSE to indicate a failure
534 BOOL WINAPI
SetThreadPriorityBoost(
535 HANDLE hthread
, /* [in] Handle to thread */
536 BOOL disable
) /* [in] TRUE to disable priority boost */
538 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
543 /**********************************************************************
544 * SetThreadAffinityMask (KERNEL32.@)
546 DWORD WINAPI
SetThreadAffinityMask( HANDLE hThread
, DWORD dwThreadAffinityMask
)
549 SERVER_START_REQ( set_thread_info
)
551 req
->handle
= hThread
;
552 req
->affinity
= dwThreadAffinityMask
;
553 req
->mask
= SET_THREAD_INFO_AFFINITY
;
554 ret
= !wine_server_call_err( req
);
555 /* FIXME: should return previous value */
562 /**********************************************************************
563 * SetThreadIdealProcessor [KERNEL32.@] Obtains timing information.
566 * Success: Value of last call to SetThreadIdealProcessor
569 DWORD WINAPI
SetThreadIdealProcessor(
570 HANDLE hThread
, /* [in] Specifies the thread of interest */
571 DWORD dwIdealProcessor
) /* [in] Specifies the new preferred processor */
573 FIXME("(%p): stub\n",hThread
);
574 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
579 /* callback for QueueUserAPC */
580 static void CALLBACK
call_user_apc( ULONG_PTR arg1
, ULONG_PTR arg2
, ULONG_PTR arg3
)
582 PAPCFUNC func
= (PAPCFUNC
)arg1
;
586 /***********************************************************************
587 * QueueUserAPC (KERNEL32.@)
589 DWORD WINAPI
QueueUserAPC( PAPCFUNC func
, HANDLE hthread
, ULONG_PTR data
)
591 NTSTATUS status
= NtQueueApcThread( hthread
, call_user_apc
, (ULONG_PTR
)func
, data
, 0 );
593 if (status
) SetLastError( RtlNtStatusToDosError(status
) );
598 /**********************************************************************
599 * GetThreadTimes [KERNEL32.@] Obtains timing information.
605 BOOL WINAPI
GetThreadTimes(
606 HANDLE thread
, /* [in] Specifies the thread of interest */
607 LPFILETIME creationtime
, /* [out] When the thread was created */
608 LPFILETIME exittime
, /* [out] When the thread was destroyed */
609 LPFILETIME kerneltime
, /* [out] Time thread spent in kernel mode */
610 LPFILETIME usertime
) /* [out] Time thread spent in user mode */
614 if (creationtime
|| exittime
)
616 /* We need to do a server call to get the creation time or exit time */
617 /* This works on any thread */
619 SERVER_START_REQ( get_thread_info
)
621 req
->handle
= thread
;
623 if ((ret
= !wine_server_call_err( req
)))
626 RtlSecondsSince1970ToTime( reply
->creation_time
, (LARGE_INTEGER
*)creationtime
);
628 RtlSecondsSince1970ToTime( reply
->exit_time
, (LARGE_INTEGER
*)exittime
);
633 if (ret
&& (kerneltime
|| usertime
))
635 /* We call times(2) for kernel time or user time */
636 /* We can only (portably) do this for the current thread */
637 if (thread
== GetCurrentThread())
641 long clocks_per_sec
= sysconf(_SC_CLK_TCK
);
646 time
= (ULONGLONG
)time_buf
.tms_stime
* 10000000 / clocks_per_sec
;
647 kerneltime
->dwHighDateTime
= time
>> 32;
648 kerneltime
->dwLowDateTime
= (DWORD
)time
;
652 time
= (ULONGLONG
)time_buf
.tms_utime
* 10000000 / clocks_per_sec
;
653 usertime
->dwHighDateTime
= time
>> 32;
654 usertime
->dwLowDateTime
= (DWORD
)time
;
659 if (kerneltime
) kerneltime
->dwHighDateTime
= kerneltime
->dwLowDateTime
= 0;
660 if (usertime
) usertime
->dwHighDateTime
= usertime
->dwLowDateTime
= 0;
661 FIXME("Cannot get kerneltime or usertime of other threads\n");
668 /**********************************************************************
669 * VWin32_BoostThreadGroup [KERNEL.535]
671 VOID WINAPI
VWin32_BoostThreadGroup( DWORD threadId
, INT boost
)
673 FIXME("(0x%08lx,%d): stub\n", threadId
, boost
);
677 /**********************************************************************
678 * VWin32_BoostThreadStatic [KERNEL.536]
680 VOID WINAPI
VWin32_BoostThreadStatic( DWORD threadId
, INT boost
)
682 FIXME("(0x%08lx,%d): stub\n", threadId
, boost
);
686 /***********************************************************************
687 * GetCurrentThread [KERNEL32.@] Gets pseudohandle for current thread
690 * Pseudohandle for the current thread
692 #undef GetCurrentThread
693 HANDLE WINAPI
GetCurrentThread(void)
695 return (HANDLE
)0xfffffffe;