blob: b3320e739bd0cbff5cea3ae1cc43735f38d8fdc8 [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>
26#include <unistd.h>
Ulrich Weigand06bf0172000-06-08 00:39:59 +000027#include <sys/time.h>
Marcus Meissnerc2606381999-04-11 15:20:29 +000028#ifdef HAVE_SYS_SYSCALL_H
29# include <sys/syscall.h>
30#endif
Ulrich Weiganda44f9f81999-04-18 13:20:43 +000031#ifdef HAVE_SYS_LWP_H
32# include <sys/lwp.h>
33#endif
34#ifdef HAVE_UCONTEXT_H
35# include <ucontext.h>
36#endif
Alexandre Julliard5016e922002-01-07 18:04:07 +000037#ifdef HAVE_SYS_MMAN_H
38#include <sys/mman.h>
39#endif
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000040#include "thread.h"
Alexandre Julliard37e95032001-07-19 00:39:09 +000041#include "wine/server.h"
Alexandre Julliard03468f71998-02-15 19:40:49 +000042#include "winbase.h"
Alexandre Julliard5016e922002-01-07 18:04:07 +000043#include "wine/winbase16.h"
Alexandre Julliard3d06d201999-09-27 10:58:45 +000044#include "wine/exception.h"
Alexandre Julliard59008672002-05-16 20:32:16 +000045#include "wine/library.h"
Alexandre Julliard0799c1a2002-03-09 23:29:33 +000046#include "wine/debug.h"
Alexandre Julliard03468f71998-02-15 19:40:49 +000047
Alexandre Julliard0799c1a2002-03-09 23:29:33 +000048WINE_DEFAULT_DEBUG_CHANNEL(thread);
Patrik Stridvallb4b9fae1999-04-19 14:56:29 +000049
Bang Jun-Young289a2522001-06-15 19:43:51 +000050#if defined(linux) || defined(HAVE_CLONE)
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000051# ifdef HAVE_SCHED_H
52# include <sched.h>
53# endif
54# ifndef CLONE_VM
55# define CLONE_VM 0x00000100
56# define CLONE_FS 0x00000200
57# define CLONE_FILES 0x00000400
58# define CLONE_SIGHAND 0x00000800
59# define CLONE_PID 0x00001000
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000060# endif /* CLONE_VM */
Bang Jun-Young289a2522001-06-15 19:43:51 +000061#endif /* linux || HAVE_CLONE */
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000062
Alexandre Julliard5016e922002-01-07 18:04:07 +000063extern void SELECTOR_FreeFs(void);
64
65struct thread_cleanup_info
66{
67 void *stack_base;
68 int stack_size;
69 int status;
70};
71
72/* temporary stacks used on thread exit */
73#define TEMP_STACK_SIZE 1024
74#define NB_TEMP_STACKS 8
75static char temp_stacks[NB_TEMP_STACKS][TEMP_STACK_SIZE];
76static LONG next_temp_stack; /* next temp stack to use */
77
Alexandre Julliard0623a6f1998-01-18 18:01:49 +000078/***********************************************************************
Alexandre Julliard54a39e21999-05-29 14:27:26 +000079 * SYSDEPS_SetCurThread
80 *
81 * Make 'thread' the current thread.
82 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +000083void SYSDEPS_SetCurThread( TEB *teb )
Alexandre Julliard54a39e21999-05-29 14:27:26 +000084{
Ulrich Weigandcebd60b1999-09-03 16:45:44 +000085#if defined(__i386__)
Alexandre Julliard54a39e21999-05-29 14:27:26 +000086 /* On the i386, the current thread is in the %fs register */
Alexandre Julliard59008672002-05-16 20:32:16 +000087 wine_set_fs( teb->teb_sel );
Ulrich Weigandcebd60b1999-09-03 16:45:44 +000088#elif defined(HAVE__LWP_CREATE)
89 /* On non-i386 Solaris, we use the LWP private pointer */
90 _lwp_setprivate( teb );
91#endif
Alexandre Julliard54a39e21999-05-29 14:27:26 +000092}
93
Alexandre Julliard5016e922002-01-07 18:04:07 +000094
95/***********************************************************************
96 * call_on_thread_stack
97 *
98 * Call a function once we switched to the thread stack.
99 */
100static void call_on_thread_stack( void *func )
101{
102 __TRY
103 {
104 void (*funcptr)(void) = func;
105 funcptr();
106 }
107 __EXCEPT(UnhandledExceptionFilter)
108 {
109 TerminateThread( GetCurrentThread(), GetExceptionCode() );
110 }
111 __ENDTRY
112 SYSDEPS_ExitThread(0); /* should never get here */
113}
114
115
116/***********************************************************************
117 * get_temp_stack
118 *
119 * Get a temporary stack address to run the thread exit code on.
120 */
121inline static char *get_temp_stack(void)
122{
123 unsigned int next = InterlockedExchangeAdd( &next_temp_stack, 1 );
124 return temp_stacks[next % NB_TEMP_STACKS];
125}
126
127
128/***********************************************************************
129 * cleanup_thread
130 *
131 * Cleanup the remains of a thread. Runs on a temporary stack.
132 */
133static void cleanup_thread( void *ptr )
134{
135 /* copy the info structure since it is on the stack we will free */
136 struct thread_cleanup_info info = *(struct thread_cleanup_info *)ptr;
137 munmap( info.stack_base, info.stack_size );
138 SELECTOR_FreeFs();
139#ifdef HAVE__LWP_CREATE
140 _lwp_exit();
141#endif
142 _exit( info.status );
143}
144
145
Alexandre Julliard54a39e21999-05-29 14:27:26 +0000146/***********************************************************************
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000147 * SYSDEPS_StartThread
148 *
149 * Startup routine for a new thread.
150 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000151static void SYSDEPS_StartThread( TEB *teb )
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000152{
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000153 SYSDEPS_SetCurThread( teb );
Alexandre Julliard875c4b31999-03-23 14:09:41 +0000154 CLIENT_InitThread();
Alexandre Julliard3d06d201999-09-27 10:58:45 +0000155 SIGNAL_Init();
156 __TRY
157 {
158 teb->startup();
159 }
160 __EXCEPT(UnhandledExceptionFilter)
161 {
162 TerminateThread( GetCurrentThread(), GetExceptionCode() );
163 }
164 __ENDTRY
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000165 SYSDEPS_ExitThread(0); /* should never get here */
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000166}
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000167
168
169/***********************************************************************
170 * SYSDEPS_SpawnThread
171 *
172 * Start running a new thread.
173 * Return -1 on error, 0 if OK.
174 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000175int SYSDEPS_SpawnThread( TEB *teb )
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000176{
Alexandre Julliard598412e2001-01-17 20:22:22 +0000177#ifdef ERRNO_LOCATION
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000178
Bang Jun-Young289a2522001-06-15 19:43:51 +0000179#if defined(linux) || defined(HAVE_CLONE)
Alexandre Julliard31741742000-03-28 18:47:24 +0000180 const int flags = CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD;
181 if (clone( (int (*)(void *))SYSDEPS_StartThread, teb->stack_top, flags, teb ) < 0)
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000182 return -1;
Alexandre Julliard8859d772001-03-01 22:13:49 +0000183 if (!(flags & CLONE_FILES)) close( teb->request_fd ); /* close the child socket in the parent */
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000184 return 0;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000185#endif
186
187#ifdef HAVE_RFORK
Alexandre Julliard31741742000-03-28 18:47:24 +0000188 const int flags = RFPROC | RFMEM; /*|RFFDG*/
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000189 void **sp = (void **)teb->stack_top;
190 *--sp = teb;
Marcus Meissnerc2606381999-04-11 15:20:29 +0000191 *--sp = 0;
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000192 *--sp = SYSDEPS_StartThread;
193 __asm__ __volatile__(
Alexandre Julliard31741742000-03-28 18:47:24 +0000194 "pushl %2;\n\t" /* flags */
Marcus Meissnerc2606381999-04-11 15:20:29 +0000195 "pushl $0;\n\t" /* 0 ? */
196 "movl %1,%%eax;\n\t" /* SYS_rfork */
197 ".byte 0x9a; .long 0; .word 7;\n\t" /* lcall 7:0... FreeBSD syscall */
198 "cmpl $0, %%edx;\n\t"
199 "je 1f;\n\t"
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000200 "movl %0,%%esp;\n\t" /* child -> new thread */
Marcus Meissnerc2606381999-04-11 15:20:29 +0000201 "ret;\n"
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000202 "1:\n\t" /* parent -> caller thread */
Marcus Meissnerc2606381999-04-11 15:20:29 +0000203 "addl $8,%%esp" :
Alexandre Julliard31741742000-03-28 18:47:24 +0000204 : "r" (sp), "g" (SYS_rfork), "g" (flags)
Marcus Meissnerc2606381999-04-11 15:20:29 +0000205 : "eax", "edx");
Alexandre Julliard8859d772001-03-01 22:13:49 +0000206 if (flags & RFFDG) close( teb->request_fd ); /* close the child socket in the parent */
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000207 return 0;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000208#endif
209
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000210#ifdef HAVE__LWP_CREATE
211 ucontext_t context;
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000212 _lwp_makecontext( &context, (void(*)(void *))SYSDEPS_StartThread, teb,
213 NULL, teb->stack_base, (char *)teb->stack_top - (char *)teb->stack_base );
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000214 if ( _lwp_create( &context, 0, NULL ) )
215 return -1;
216 return 0;
217#endif
218
Alexandre Julliard598412e2001-01-17 20:22:22 +0000219#endif /* ERRNO_LOCATION */
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000220
Alexandre Julliard15657091999-05-23 10:25:25 +0000221 FIXME("CreateThread: stub\n" );
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000222 return 0;
223}
224
225
Alexandre Julliard5016e922002-01-07 18:04:07 +0000226/***********************************************************************
227 * SYSDEPS_CallOnStack
228 */
229void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg ) WINE_NORETURN;
230#ifdef __i386__
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000231#ifdef __GNUC__
Alexandre Julliard5016e922002-01-07 18:04:07 +0000232__ASM_GLOBAL_FUNC( SYSDEPS_CallOnStack,
233 "movl 4(%esp),%ecx\n\t" /* func */
234 "movl 8(%esp),%edx\n\t" /* arg */
235 ".byte 0x64\n\tmovl 0x04,%esp\n\t" /* teb->stack_top */
236 "pushl %edx\n\t"
237 "xorl %ebp,%ebp\n\t"
238 "call *%ecx\n\t"
239 "int $3" /* we never return here */ );
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000240#elif defined(_MSC_VER)
241__declspec(naked) void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg )
242{
243 __asm mov ecx, 4[esp];
244 __asm mov edx, 8[esp];
245 __asm mov fs:[0x04], esp;
246 __asm push edx;
247 __asm xor ebp, ebp;
248 __asm call [ecx];
249 __asm int 3;
250}
251#endif /* defined(__GNUC__) || defined(_MSC_VER) */
252#else /* defined(__i386__) */
Alexandre Julliard5016e922002-01-07 18:04:07 +0000253void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg )
254{
255 func( arg );
256 while(1); /* avoid warning */
257}
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000258#endif /* defined(__i386__) */
Alexandre Julliard5016e922002-01-07 18:04:07 +0000259
260
261/***********************************************************************
262 * SYSDEPS_SwitchToThreadStack
263 */
264void SYSDEPS_SwitchToThreadStack( void (*func)(void) )
265{
266 SYSDEPS_CallOnStack( call_on_thread_stack, func );
267}
268
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000269
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000270/***********************************************************************
271 * SYSDEPS_ExitThread
272 *
273 * Exit a running thread; must not return.
274 */
Alexandre Julliard12f29b52000-03-17 15:16:57 +0000275void SYSDEPS_ExitThread( int status )
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000276{
Alexandre Julliard5016e922002-01-07 18:04:07 +0000277 TEB *teb = NtCurrentTeb();
278 struct thread_cleanup_info info;
279 MEMORY_BASIC_INFORMATION meminfo;
280
281 FreeSelector16( teb->stack_sel );
282 VirtualQuery( teb->stack_top, &meminfo, sizeof(meminfo) );
283 info.stack_base = meminfo.AllocationBase;
284 info.stack_size = meminfo.RegionSize + ((char *)teb->stack_top - (char *)meminfo.AllocationBase);
285 info.status = status;
286
287 SIGNAL_Reset();
288
289 VirtualFree( teb->stack_base, 0, MEM_RELEASE | MEM_SYSTEM );
290 close( teb->wait_fd[0] );
291 close( teb->wait_fd[1] );
292 close( teb->reply_fd );
293 close( teb->request_fd );
294 teb->stack_low = get_temp_stack();
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000295 teb->stack_top = (char *) teb->stack_low + TEMP_STACK_SIZE;
Alexandre Julliard5016e922002-01-07 18:04:07 +0000296 SYSDEPS_CallOnStack( cleanup_thread, &info );
297}
298
299
300/***********************************************************************
301 * SYSDEPS_AbortThread
302 *
303 * Same as SYSDEPS_ExitThread, but must not do anything that requires a server call.
304 */
305void SYSDEPS_AbortThread( int status )
306{
307 SIGNAL_Reset();
308 close( NtCurrentTeb()->wait_fd[0] );
309 close( NtCurrentTeb()->wait_fd[1] );
310 close( NtCurrentTeb()->reply_fd );
311 close( NtCurrentTeb()->request_fd );
Alexandre Julliard9a0e28f2000-03-25 19:14:37 +0000312#ifdef HAVE__LWP_CREATE
Ulrich Weiganda44f9f81999-04-18 13:20:43 +0000313 _lwp_exit();
314#endif
Alexandre Julliard5016e922002-01-07 18:04:07 +0000315 for (;;) /* avoid warning */
316 _exit( status );
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000317}
318
319
320/**********************************************************************
Patrik Stridvalld0a41772001-02-14 23:11:17 +0000321 * NtCurrentTeb (NTDLL.@)
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000322 *
323 * This will crash and burn if called before threading is initialized
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000324 */
Patrik Stridvall441a7dc2002-04-29 18:37:36 +0000325#if defined(__i386__) && defined(__GNUC__)
Alexandre Julliard916f9752000-02-26 16:51:13 +0000326__ASM_GLOBAL_FUNC( NtCurrentTeb, ".byte 0x64\n\tmovl 0x18,%eax\n\tret" );
Patrik Stridvall441a7dc2002-04-29 18:37:36 +0000327#elif defined(__i386__) && defined(_MSC_VER)
Patrik Stridvall3751ff02002-05-04 18:37:08 +0000328/* Nothing needs to be done. MS C "magically" exports the inline version from winnt.h */
Alexandre Julliard916f9752000-02-26 16:51:13 +0000329#elif defined(HAVE__LWP_CREATE)
Patrik Stridvalldb923052001-07-24 00:58:52 +0000330/***********************************************************************
331 * NtCurrentTeb (NTDLL.@)
332 */
Alexandre Julliard0a860a01999-06-22 11:43:42 +0000333struct _TEB * WINAPI NtCurrentTeb(void)
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000334{
Alexandre Julliard916f9752000-02-26 16:51:13 +0000335 extern void *_lwp_getprivate(void);
336 return (struct _TEB *)_lwp_getprivate();
Alexandre Julliard0623a6f1998-01-18 18:01:49 +0000337}
Alexandre Julliard916f9752000-02-26 16:51:13 +0000338#else
339# error NtCurrentTeb not defined for this architecture
340#endif /* __i386__ */