blob: df3a870bd1d7f8572d81c2a68e128a71b109e4f2 [file] [log] [blame]
Alexandre Julliard0623a6f1998-01-18 18:01:49 +00001/*
2 * System-dependent scheduler support
3 *
4 * Copyright 1998 Alexandre Julliard
Alexandre Julliard0799c1a2002-03-09 23:29:33 +00005 *
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
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000019 */
20
François Gouget14259412001-11-06 20:57:11 +000021#include "config.h"
Francois Gouget386cf6e2001-10-14 16:25:47 +000022#include "wine/port.h"
Marcus Meissnerc2606381999-04-11 15:20:29 +000023
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000024#include <signal.h>
25#include <stdio.h>
Patrik Stridvalld016f812002-08-17 00:43:16 +000026#ifdef HAVE_UNISTD_H
27# include <unistd.h>
28#endif
29#ifdef HAVE_SYS_TIME_H
30# include <sys/time.h>
31#endif
Marcus Meissnerc2606381999-04-11 15:20:29 +000032#ifdef HAVE_SYS_SYSCALL_H
33# include <sys/syscall.h>
34#endif
Ulrich Weiganda44f9f81999-04-18 13:20:43 +000035#ifdef HAVE_SYS_LWP_H
36# include <sys/lwp.h>
37#endif
38#ifdef HAVE_UCONTEXT_H
39# include <ucontext.h>
40#endif
Alexandre Julliard5016e922002-01-07 18:04:07 +000041#ifdef HAVE_SYS_MMAN_H
42#include <sys/mman.h>
43#endif
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000044#include "thread.h"
Alexandre Julliard37e95032001-07-19 00:39:09 +000045#include "wine/server.h"
Alexandre Julliard03468f71998-02-15 19:40:49 +000046#include "winbase.h"
Alexandre Julliard5016e922002-01-07 18:04:07 +000047#include "wine/winbase16.h"
Alexandre Julliard3d06d201999-09-27 10:58:45 +000048#include "wine/exception.h"
Alexandre Julliard59008672002-05-16 20:32:16 +000049#include "wine/library.h"
Alexandre Julliard0799c1a2002-03-09 23:29:33 +000050#include "wine/debug.h"
Alexandre Julliard03468f71998-02-15 19:40:49 +000051
Alexandre Julliard0799c1a2002-03-09 23:29:33 +000052WINE_DEFAULT_DEBUG_CHANNEL(thread);
Patrik Stridvallb4b9fae1999-04-19 14:56:29 +000053
Bang Jun-Young289a2522001-06-15 19:43:51 +000054#if defined(linux) || defined(HAVE_CLONE)
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000055# ifdef HAVE_SCHED_H
56# include <sched.h>
57# endif
58# ifndef CLONE_VM
59# define CLONE_VM 0x00000100
60# define CLONE_FS 0x00000200
61# define CLONE_FILES 0x00000400
62# define CLONE_SIGHAND 0x00000800
63# define CLONE_PID 0x00001000
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000064# endif /* CLONE_VM */
Bang Jun-Young289a2522001-06-15 19:43:51 +000065#endif /* linux || HAVE_CLONE */
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000066
Alexandre Julliard5016e922002-01-07 18:04:07 +000067extern void SELECTOR_FreeFs(void);
68
69struct thread_cleanup_info
70{
71 void *stack_base;
72 int stack_size;
73 int status;
74};
75
76/* temporary stacks used on thread exit */
77#define TEMP_STACK_SIZE 1024
78#define NB_TEMP_STACKS 8
79static char temp_stacks[NB_TEMP_STACKS][TEMP_STACK_SIZE];
80static LONG next_temp_stack; /* next temp stack to use */
81
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000082/***********************************************************************
Alexandre Julliard54a39e21999-05-29 14:27:26 +000083 * SYSDEPS_SetCurThread
84 *
85 * Make 'thread' the current thread.
86 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +000087void SYSDEPS_SetCurThread( TEB *teb )
Alexandre Julliard54a39e21999-05-29 14:27:26 +000088{
Ulrich Weigandcebd60b1999-09-03 16:45:44 +000089#if defined(__i386__)
Alexandre Julliard54a39e21999-05-29 14:27:26 +000090 /* On the i386, the current thread is in the %fs register */
Alexandre Julliard59008672002-05-16 20:32:16 +000091 wine_set_fs( teb->teb_sel );
Alexandre Julliardc1dec292002-08-06 23:51:25 +000092#elif defined(__powerpc__)
93 /* On PowerPC, the current TEB is in the gpr13 register */
Marcus Meissner24561492002-08-20 00:00:35 +000094 __asm__ __volatile__("mr 2, %0" : : "r" (teb));
Ulrich Weigandcebd60b1999-09-03 16:45:44 +000095#elif defined(HAVE__LWP_CREATE)
96 /* On non-i386 Solaris, we use the LWP private pointer */
97 _lwp_setprivate( teb );
98#endif
Alexandre Julliard54a39e21999-05-29 14:27:26 +000099}
100
Alexandre Julliard5016e922002-01-07 18:04:07 +0000101
102/***********************************************************************
103 * call_on_thread_stack
104 *
105 * Call a function once we switched to the thread stack.
106 */
107static void call_on_thread_stack( void *func )
108{
109 __TRY
110 {
111 void (*funcptr)(void) = func;
112 funcptr();
113 }
114 __EXCEPT(UnhandledExceptionFilter)
115 {
116 TerminateThread( GetCurrentThread(), GetExceptionCode() );
117 }
118 __ENDTRY
119 SYSDEPS_ExitThread(0); /* should never get here */
120}
121
122
123/***********************************************************************
124 * get_temp_stack
125 *
126 * Get a temporary stack address to run the thread exit code on.
127 */
128inline static char *get_temp_stack(void)
129{
130 unsigned int next = InterlockedExchangeAdd( &next_temp_stack, 1 );
131 return temp_stacks[next % NB_TEMP_STACKS];
132}
133
134
135/***********************************************************************
136 * cleanup_thread
137 *
138 * Cleanup the remains of a thread. Runs on a temporary stack.
139 */
140static void cleanup_thread( void *ptr )
141{
142 /* copy the info structure since it is on the stack we will free */
143 struct thread_cleanup_info info = *(struct thread_cleanup_info *)ptr;
144 munmap( info.stack_base, info.stack_size );
145 SELECTOR_FreeFs();
146#ifdef HAVE__LWP_CREATE
147 _lwp_exit();
148#endif
149 _exit( info.status );
150}
151
152
Alexandre Julliard54a39e21999-05-29 14:27:26 +0000153/***********************************************************************
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000154 * SYSDEPS_StartThread
155 *
156 * Startup routine for a new thread.
157 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000158static void SYSDEPS_StartThread( TEB *teb )
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000159{
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000160 SYSDEPS_SetCurThread( teb );
Alexandre Julliard875c4b31999-03-23 14:09:41 +0000161 CLIENT_InitThread();
Alexandre Julliard3d06d201999-09-27 10:58:45 +0000162 SIGNAL_Init();
163 __TRY
164 {
165 teb->startup();
166 }
167 __EXCEPT(UnhandledExceptionFilter)
168 {
169 TerminateThread( GetCurrentThread(), GetExceptionCode() );
170 }
171 __ENDTRY
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000172 SYSDEPS_ExitThread(0); /* should never get here */
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000173}
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000174
175
176/***********************************************************************
177 * SYSDEPS_SpawnThread
178 *
179 * Start running a new thread.
180 * Return -1 on error, 0 if OK.
181 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000182int SYSDEPS_SpawnThread( TEB *teb )
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000183{
Alexandre Julliard598412e2001-01-17 20:22:22 +0000184#ifdef ERRNO_LOCATION
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000185
Bang Jun-Young289a2522001-06-15 19:43:51 +0000186#if defined(linux) || defined(HAVE_CLONE)
Alexandre Julliard31741742000-03-28 18:47:24 +0000187 const int flags = CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD;
188 if (clone( (int (*)(void *))SYSDEPS_StartThread, teb->stack_top, flags, teb ) < 0)
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000189 return -1;
Alexandre Julliard8859d772001-03-01 22:13:49 +0000190 if (!(flags & CLONE_FILES)) close( teb->request_fd ); /* close the child socket in the parent */
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000191 return 0;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000192#endif
193
194#ifdef HAVE_RFORK
Alexandre Julliard31741742000-03-28 18:47:24 +0000195 const int flags = RFPROC | RFMEM; /*|RFFDG*/
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000196 void **sp = (void **)teb->stack_top;
197 *--sp = teb;
Marcus Meissnerc2606381999-04-11 15:20:29 +0000198 *--sp = 0;
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000199 *--sp = SYSDEPS_StartThread;
200 __asm__ __volatile__(
Alexandre Julliard31741742000-03-28 18:47:24 +0000201 "pushl %2;\n\t" /* flags */
Marcus Meissnerc2606381999-04-11 15:20:29 +0000202 "pushl $0;\n\t" /* 0 ? */
203 "movl %1,%%eax;\n\t" /* SYS_rfork */
204 ".byte 0x9a; .long 0; .word 7;\n\t" /* lcall 7:0... FreeBSD syscall */
205 "cmpl $0, %%edx;\n\t"
206 "je 1f;\n\t"
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000207 "movl %0,%%esp;\n\t" /* child -> new thread */
Marcus Meissnerc2606381999-04-11 15:20:29 +0000208 "ret;\n"
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000209 "1:\n\t" /* parent -> caller thread */
Marcus Meissnerc2606381999-04-11 15:20:29 +0000210 "addl $8,%%esp" :
Alexandre Julliard31741742000-03-28 18:47:24 +0000211 : "r" (sp), "g" (SYS_rfork), "g" (flags)
Marcus Meissnerc2606381999-04-11 15:20:29 +0000212 : "eax", "edx");
Alexandre Julliard8859d772001-03-01 22:13:49 +0000213 if (flags & RFFDG) close( teb->request_fd ); /* close the child socket in the parent */
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000214 return 0;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000215#endif
216
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000217#ifdef HAVE__LWP_CREATE
218 ucontext_t context;
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000219 _lwp_makecontext( &context, (void(*)(void *))SYSDEPS_StartThread, teb,
220 NULL, teb->stack_base, (char *)teb->stack_top - (char *)teb->stack_base );
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000221 if ( _lwp_create( &context, 0, NULL ) )
222 return -1;
223 return 0;
224#endif
225
Alexandre Julliard598412e2001-01-17 20:22:22 +0000226#endif /* ERRNO_LOCATION */
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000227
Alexandre Julliard15657091999-05-23 10:25:25 +0000228 FIXME("CreateThread: stub\n" );
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000229 return 0;
230}
231
232
Alexandre Julliard5016e922002-01-07 18:04:07 +0000233/***********************************************************************
234 * SYSDEPS_CallOnStack
235 */
Patrik Stridvalle29345c2002-10-01 18:07:37 +0000236void DECLSPEC_NORETURN SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg );
Alexandre Julliard5016e922002-01-07 18:04:07 +0000237#ifdef __i386__
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000238#ifdef __GNUC__
Alexandre Julliard5016e922002-01-07 18:04:07 +0000239__ASM_GLOBAL_FUNC( SYSDEPS_CallOnStack,
240 "movl 4(%esp),%ecx\n\t" /* func */
241 "movl 8(%esp),%edx\n\t" /* arg */
242 ".byte 0x64\n\tmovl 0x04,%esp\n\t" /* teb->stack_top */
243 "pushl %edx\n\t"
244 "xorl %ebp,%ebp\n\t"
245 "call *%ecx\n\t"
246 "int $3" /* we never return here */ );
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000247#elif defined(_MSC_VER)
248__declspec(naked) void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg )
249{
250 __asm mov ecx, 4[esp];
251 __asm mov edx, 8[esp];
252 __asm mov fs:[0x04], esp;
253 __asm push edx;
254 __asm xor ebp, ebp;
255 __asm call [ecx];
256 __asm int 3;
257}
258#endif /* defined(__GNUC__) || defined(_MSC_VER) */
Alexandre Julliardc1dec292002-08-06 23:51:25 +0000259#else /* !defined(__i386__) */
Alexandre Julliard5016e922002-01-07 18:04:07 +0000260void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg )
261{
262 func( arg );
263 while(1); /* avoid warning */
264}
Alexandre Julliardc1dec292002-08-06 23:51:25 +0000265#endif /* !defined(__i386__) */
Alexandre Julliard5016e922002-01-07 18:04:07 +0000266
267
268/***********************************************************************
269 * SYSDEPS_SwitchToThreadStack
270 */
271void SYSDEPS_SwitchToThreadStack( void (*func)(void) )
272{
273 SYSDEPS_CallOnStack( call_on_thread_stack, func );
274}
275
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000276
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000277/***********************************************************************
278 * SYSDEPS_ExitThread
279 *
280 * Exit a running thread; must not return.
281 */
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000282void SYSDEPS_ExitThread( int status )
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000283{
Alexandre Julliard5016e922002-01-07 18:04:07 +0000284 TEB *teb = NtCurrentTeb();
285 struct thread_cleanup_info info;
286 MEMORY_BASIC_INFORMATION meminfo;
287
288 FreeSelector16( teb->stack_sel );
289 VirtualQuery( teb->stack_top, &meminfo, sizeof(meminfo) );
290 info.stack_base = meminfo.AllocationBase;
291 info.stack_size = meminfo.RegionSize + ((char *)teb->stack_top - (char *)meminfo.AllocationBase);
292 info.status = status;
293
294 SIGNAL_Reset();
295
296 VirtualFree( teb->stack_base, 0, MEM_RELEASE | MEM_SYSTEM );
297 close( teb->wait_fd[0] );
298 close( teb->wait_fd[1] );
299 close( teb->reply_fd );
300 close( teb->request_fd );
301 teb->stack_low = get_temp_stack();
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000302 teb->stack_top = (char *) teb->stack_low + TEMP_STACK_SIZE;
Alexandre Julliard5016e922002-01-07 18:04:07 +0000303 SYSDEPS_CallOnStack( cleanup_thread, &info );
304}
305
306
307/***********************************************************************
308 * SYSDEPS_AbortThread
309 *
310 * Same as SYSDEPS_ExitThread, but must not do anything that requires a server call.
311 */
312void SYSDEPS_AbortThread( int status )
313{
314 SIGNAL_Reset();
315 close( NtCurrentTeb()->wait_fd[0] );
316 close( NtCurrentTeb()->wait_fd[1] );
317 close( NtCurrentTeb()->reply_fd );
318 close( NtCurrentTeb()->request_fd );
Alexandre Julliard9a0e28f2000-03-25 19:14:37 +0000319#ifdef HAVE__LWP_CREATE
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000320 _lwp_exit();
321#endif
Alexandre Julliard5016e922002-01-07 18:04:07 +0000322 for (;;) /* avoid warning */
323 _exit( status );
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000324}
325
326
327/**********************************************************************
Patrik Stridvalld0a41772001-02-14 23:11:17 +0000328 * NtCurrentTeb (NTDLL.@)
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000329 *
330 * This will crash and burn if called before threading is initialized
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000331 */
Patrik Stridvall441a7dc2002-04-29 18:37:36 +0000332#if defined(__i386__) && defined(__GNUC__)
Alexandre Julliard916f9752000-02-26 16:51:13 +0000333__ASM_GLOBAL_FUNC( NtCurrentTeb, ".byte 0x64\n\tmovl 0x18,%eax\n\tret" );
Patrik Stridvall441a7dc2002-04-29 18:37:36 +0000334#elif defined(__i386__) && defined(_MSC_VER)
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000335/* Nothing needs to be done. MS C "magically" exports the inline version from winnt.h */
Alexandre Julliard916f9752000-02-26 16:51:13 +0000336#elif defined(HAVE__LWP_CREATE)
Patrik Stridvalldb923052001-07-24 00:58:52 +0000337/***********************************************************************
338 * NtCurrentTeb (NTDLL.@)
339 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000340struct _TEB * WINAPI NtCurrentTeb(void)
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000341{
Alexandre Julliard916f9752000-02-26 16:51:13 +0000342 extern void *_lwp_getprivate(void);
343 return (struct _TEB *)_lwp_getprivate();
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000344}
Alexandre Julliardc1dec292002-08-06 23:51:25 +0000345#elif defined(__powerpc__)
Marcus Meissner24561492002-08-20 00:00:35 +0000346__ASM_GLOBAL_FUNC( NtCurrentTeb, "\n\tmr 3,2\n\tblr" );
Alexandre Julliard916f9752000-02-26 16:51:13 +0000347#else
348# error NtCurrentTeb not defined for this architecture
349#endif /* __i386__ */