Added wine_pthread_create_thread and wine_pthread_exit_thread to the

pthread support, and removed the corresponding SYSDEPS functions.
Moved stack allocation for new threads to wine_pthread_create_thread
to allow more flexibility.
This commit is contained in:
Alexandre Julliard 2003-11-05 23:31:11 +00:00
parent 8defcd386c
commit 8574412e1e
8 changed files with 321 additions and 205 deletions

View File

@ -24,6 +24,7 @@
#include <assert.h> #include <assert.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h> #include <stdarg.h>
#include <signal.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef HAVE_SYS_TIMES_H #ifdef HAVE_SYS_TIMES_H
#include <sys/times.h> #include <sys/times.h>
@ -41,6 +42,7 @@
#include "thread.h" #include "thread.h"
#include "wine/winbase16.h" #include "wine/winbase16.h"
#include "wine/library.h" #include "wine/library.h"
#include "wine/pthread.h"
#include "wine/server.h" #include "wine/server.h"
#include "wine/debug.h" #include "wine/debug.h"
@ -197,11 +199,45 @@ void WINAPI ExitThread( DWORD code ) /* [in] Exit code for this thread */
} }
else else
{ {
struct wine_pthread_thread_info info;
sigset_t block_set;
ULONG size;
LdrShutdownThread(); LdrShutdownThread();
RtlAcquirePebLock(); RtlAcquirePebLock();
RemoveEntryList( &NtCurrentTeb()->TlsLinks ); RemoveEntryList( &NtCurrentTeb()->TlsLinks );
RtlReleasePebLock(); RtlReleasePebLock();
SYSDEPS_ExitThread( code );
info.stack_base = NtCurrentTeb()->DeallocationStack;
info.teb_base = NtCurrentTeb();
info.teb_sel = wine_get_fs();
info.exit_status = code;
size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &info.stack_base, &size, MEM_RELEASE | MEM_SYSTEM );
info.stack_size = size;
size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &info.teb_base, &size, MEM_RELEASE | MEM_SYSTEM );
info.teb_size = size;
/* block the async signals */
sigemptyset( &block_set );
sigaddset( &block_set, SIGALRM );
sigaddset( &block_set, SIGIO );
sigaddset( &block_set, SIGINT );
sigaddset( &block_set, SIGHUP );
sigaddset( &block_set, SIGUSR1 );
sigaddset( &block_set, SIGUSR2 );
sigaddset( &block_set, SIGTERM );
sigprocmask( SIG_BLOCK, &block_set, NULL );
close( NtCurrentTeb()->wait_fd[0] );
close( NtCurrentTeb()->wait_fd[1] );
close( NtCurrentTeb()->reply_fd );
close( NtCurrentTeb()->request_fd );
wine_pthread_exit_thread( &info );
} }
} }

View File

@ -4,7 +4,7 @@ TOPOBJDIR = ../..
SRCDIR = @srcdir@ SRCDIR = @srcdir@
VPATH = @srcdir@ VPATH = @srcdir@
MODULE = ntdll.dll MODULE = ntdll.dll
EXTRALIBS = $(LIBUNICODE) @LIBPTHREAD@ EXTRALIBS = $(LIBUNICODE)
C_SRCS = \ C_SRCS = \
cdrom.c \ cdrom.c \

View File

@ -1082,6 +1082,5 @@
@ cdecl MODULE_DllThreadAttach(ptr) @ cdecl MODULE_DllThreadAttach(ptr)
@ cdecl MODULE_GetLoadOrderA(ptr str str long) @ cdecl MODULE_GetLoadOrderA(ptr str str long)
@ cdecl MODULE_GetLoadOrderW(ptr wstr wstr long) @ cdecl MODULE_GetLoadOrderW(ptr wstr wstr long)
@ cdecl SYSDEPS_ExitThread(long)
@ cdecl VERSION_Init(wstr) @ cdecl VERSION_Init(wstr)
@ cdecl VIRTUAL_SetFaultHandler(ptr ptr ptr) @ cdecl VIRTUAL_SetFaultHandler(ptr ptr ptr)

View File

@ -55,21 +55,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(thread); WINE_DEFAULT_DEBUG_CHANNEL(thread);
struct thread_cleanup_info
{
void *stack_base;
ULONG stack_size;
void *teb_base;
ULONG teb_size;
int status;
};
/* temporary stacks used on thread exit */
#define TEMP_STACK_SIZE 1024
#define NB_TEMP_STACKS 8
static char temp_stacks[NB_TEMP_STACKS][TEMP_STACK_SIZE];
static LONG next_temp_stack; /* next temp stack to use */
/*********************************************************************** /***********************************************************************
* SYSDEPS_SetCurThread * SYSDEPS_SetCurThread
@ -97,148 +82,7 @@ void SYSDEPS_SetCurThread( TEB *teb )
/* On non-i386 Solaris, we use the LWP private pointer */ /* On non-i386 Solaris, we use the LWP private pointer */
_lwp_setprivate( teb ); _lwp_setprivate( teb );
#endif #endif
#ifdef HAVE_NPTL
teb->pthread_data = (void *)pthread_self();
#else
wine_pthread_init_thread(); wine_pthread_init_thread();
#endif
}
/***********************************************************************
* get_temp_stack
*
* Get a temporary stack address to run the thread exit code on.
*/
inline static char *get_temp_stack(void)
{
unsigned int next = interlocked_xchg_add( &next_temp_stack, 1 );
return temp_stacks[next % NB_TEMP_STACKS] + TEMP_STACK_SIZE;
}
/***********************************************************************
* cleanup_thread
*
* Cleanup the remains of a thread. Runs on a temporary stack.
*/
static void cleanup_thread( void *ptr )
{
/* copy the info structure since it is on the stack we will free */
struct thread_cleanup_info info = *(struct thread_cleanup_info *)ptr;
munmap( info.stack_base, info.stack_size );
munmap( info.teb_base, info.teb_size );
wine_ldt_free_fs( wine_get_fs() );
#ifdef HAVE__LWP_CREATE
_lwp_exit();
#endif
_exit( info.status );
}
/***********************************************************************
* SYSDEPS_SpawnThread
*
* Start running a new thread.
* Return -1 on error, 0 if OK.
*/
int SYSDEPS_SpawnThread( void (*func)(TEB *), TEB *teb )
{
#ifdef HAVE_NPTL
pthread_t id;
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setstack( &attr, teb->DeallocationStack,
(char *)teb->Tib.StackBase - (char *)teb->DeallocationStack );
if (pthread_create( &id, &attr, (void * (*)(void *))func, teb )) return -1;
return 0;
#elif defined(HAVE_CLONE)
if (clone( (int (*)(void *))func, teb->Tib.StackBase,
CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, teb ) < 0)
return -1;
return 0;
#elif defined(HAVE_RFORK)
void **sp = (void **)teb->Tib.StackBase;
*--sp = teb;
*--sp = 0;
*--sp = func;
__asm__ __volatile__(
"pushl %2;\n\t" /* flags */
"pushl $0;\n\t" /* 0 ? */
"movl %1,%%eax;\n\t" /* SYS_rfork */
".byte 0x9a; .long 0; .word 7;\n\t" /* lcall 7:0... FreeBSD syscall */
"cmpl $0, %%edx;\n\t"
"je 1f;\n\t"
"movl %0,%%esp;\n\t" /* child -> new thread */
"ret;\n"
"1:\n\t" /* parent -> caller thread */
"addl $8,%%esp" :
: "r" (sp), "g" (SYS_rfork), "g" (RFPROC | RFMEM)
: "eax", "edx");
return 0;
#elif defined(HAVE__LWP_CREATE)
ucontext_t context;
_lwp_makecontext( &context, (void(*)(void *))func, teb,
NULL, teb->DeallocationStack, (char *)teb->Tib.StackBase - (char *)teb->DeallocationStack );
if ( _lwp_create( &context, 0, NULL ) )
return -1;
return 0;
#endif
FIXME("CreateThread: stub\n" );
return -1;
}
/***********************************************************************
* SYSDEPS_ExitThread
*
* Exit a running thread; must not return.
*/
void SYSDEPS_ExitThread( int status )
{
#ifdef HAVE_NPTL
static TEB *teb_to_free;
TEB *free_teb;
if ((free_teb = interlocked_xchg_ptr( (void **)&teb_to_free, NtCurrentTeb() )) != NULL)
{
DWORD size = 0;
void *ptr;
TRACE("freeing prev teb %p stack %p fs %04x\n",
free_teb, free_teb->DeallocationStack, free_teb->teb_sel );
pthread_join( (pthread_t)free_teb->pthread_data, &ptr );
wine_ldt_free_fs( free_teb->teb_sel );
ptr = free_teb->DeallocationStack;
NtFreeVirtualMemory( GetCurrentProcess(), &ptr, &size, MEM_RELEASE );
ptr = free_teb;
NtFreeVirtualMemory( GetCurrentProcess(), &ptr, &size, MEM_RELEASE | MEM_SYSTEM );
munmap( ptr, size );
}
SYSDEPS_AbortThread( status );
#else
struct thread_cleanup_info info;
SIGNAL_Block();
info.status = status;
info.stack_base = NtCurrentTeb()->DeallocationStack;
info.stack_size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &info.stack_base,
&info.stack_size, MEM_RELEASE | MEM_SYSTEM );
info.teb_base = NtCurrentTeb();
info.teb_size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &info.teb_base,
&info.teb_size, MEM_RELEASE | MEM_SYSTEM );
close( NtCurrentTeb()->wait_fd[0] );
close( NtCurrentTeb()->wait_fd[1] );
close( NtCurrentTeb()->reply_fd );
close( NtCurrentTeb()->request_fd );
wine_switch_to_stack( cleanup_thread, &info, get_temp_stack() );
#endif
} }
@ -254,15 +98,7 @@ void SYSDEPS_AbortThread( int status )
close( NtCurrentTeb()->wait_fd[1] ); close( NtCurrentTeb()->wait_fd[1] );
close( NtCurrentTeb()->reply_fd ); close( NtCurrentTeb()->reply_fd );
close( NtCurrentTeb()->request_fd ); close( NtCurrentTeb()->request_fd );
#ifdef HAVE_NPTL wine_pthread_abort_thread( status );
pthread_exit( (void *)status );
#endif
SIGNAL_Reset();
#ifdef HAVE__LWP_CREATE
_lwp_exit();
#endif
for (;;) /* avoid warning */
_exit( status );
} }
/*********************************************************************** /***********************************************************************

View File

@ -31,6 +31,7 @@
#include "winternl.h" #include "winternl.h"
#include "wine/library.h" #include "wine/library.h"
#include "wine/server.h" #include "wine/server.h"
#include "wine/pthread.h"
#include "wine/debug.h" #include "wine/debug.h"
#include "ntdll_misc.h" #include "ntdll_misc.h"
@ -60,6 +61,7 @@ static TEB *alloc_teb( ULONG *size )
return NULL; return NULL;
} }
teb->Tib.ExceptionList = (void *)~0UL; teb->Tib.ExceptionList = (void *)~0UL;
teb->Tib.StackBase = (void *)~0UL;
teb->Tib.Self = &teb->Tib; teb->Tib.Self = &teb->Tib;
teb->Peb = &peb; teb->Peb = &peb;
teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer;
@ -76,8 +78,6 @@ static inline void free_teb( TEB *teb )
ULONG size = 0; ULONG size = 0;
void *addr = teb; void *addr = teb;
if (teb->DeallocationStack)
NtFreeVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size, MEM_RELEASE );
NtFreeVirtualMemory( GetCurrentProcess(), &addr, &size, MEM_RELEASE ); NtFreeVirtualMemory( GetCurrentProcess(), &addr, &size, MEM_RELEASE );
wine_ldt_free_fs( teb->teb_sel ); wine_ldt_free_fs( teb->teb_sel );
munmap( teb, SIGNAL_STACK_SIZE + sizeof(TEB) ); munmap( teb, SIGNAL_STACK_SIZE + sizeof(TEB) );
@ -110,7 +110,6 @@ void thread_init(void)
InitializeListHead( &tls_links ); InitializeListHead( &tls_links );
teb = alloc_teb( &size ); teb = alloc_teb( &size );
teb->Tib.StackBase = (void *)~0UL;
teb->tibflags = TEBF_WIN32; teb->tibflags = TEBF_WIN32;
teb->request_fd = -1; teb->request_fd = -1;
teb->reply_fd = -1; teb->reply_fd = -1;
@ -143,19 +142,35 @@ void thread_init(void)
* *
* Startup routine for a newly created thread. * Startup routine for a newly created thread.
*/ */
static void start_thread( TEB *teb ) static void start_thread( struct wine_pthread_thread_info *info )
{ {
TEB *teb = info->teb_base;
LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)teb->entry_point; LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)teb->entry_point;
struct debug_info info; struct debug_info debug_info;
ULONG size;
info.str_pos = info.strings; debug_info.str_pos = debug_info.strings;
info.out_pos = info.output; debug_info.out_pos = debug_info.output;
teb->debug_info = &info; teb->debug_info = &debug_info;
SYSDEPS_SetCurThread( teb ); SYSDEPS_SetCurThread( teb );
SIGNAL_Init(); SIGNAL_Init();
wine_server_init_thread(); wine_server_init_thread();
/* allocate a memory view for the stack */
size = info->stack_size;
NtAllocateVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, info->stack_base,
&size, MEM_SYSTEM, PAGE_EXECUTE_READWRITE );
/* limit is lower than base since the stack grows down */
teb->Tib.StackBase = (char *)info->stack_base + info->stack_size;
teb->Tib.StackLimit = info->stack_base;
/* setup the guard page */
size = 1;
NtProtectVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size,
PAGE_EXECUTE_READWRITE | PAGE_GUARD, NULL );
RtlFreeHeap( GetProcessHeap(), 0, info );
RtlAcquirePebLock(); RtlAcquirePebLock();
InsertHeadList( &tls_links, &teb->TlsLinks ); InsertHeadList( &tls_links, &teb->TlsLinks );
RtlReleasePebLock(); RtlReleasePebLock();
@ -173,11 +188,11 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
PRTL_THREAD_START_ROUTINE start, void *param, PRTL_THREAD_START_ROUTINE start, void *param,
HANDLE *handle_ptr, CLIENT_ID *id ) HANDLE *handle_ptr, CLIENT_ID *id )
{ {
struct wine_pthread_thread_info *info = NULL;
HANDLE handle = 0; HANDLE handle = 0;
TEB *teb = NULL; TEB *teb = NULL;
DWORD tid = 0; DWORD tid = 0;
ULONG size; ULONG size;
void *base;
int request_pipe[2]; int request_pipe[2];
NTSTATUS status; NTSTATUS status;
@ -201,6 +216,12 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
if (status) goto error; if (status) goto error;
if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) )))
{
status = STATUS_NO_MEMORY;
goto error;
}
if (!(teb = alloc_teb( &size ))) if (!(teb = alloc_teb( &size )))
{ {
status = STATUS_NO_MEMORY; status = STATUS_NO_MEMORY;
@ -219,8 +240,10 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
teb->entry_arg = param; teb->entry_arg = param;
teb->htask16 = NtCurrentTeb()->htask16; teb->htask16 = NtCurrentTeb()->htask16;
NtAllocateVirtualMemory( GetCurrentProcess(), &base, teb, &size, NtAllocateVirtualMemory( GetCurrentProcess(), &info->teb_base, teb, &size,
MEM_SYSTEM, PAGE_EXECUTE_READWRITE ); MEM_SYSTEM, PAGE_EXECUTE_READWRITE );
info->teb_size = size;
info->teb_sel = teb->teb_sel;
if (!stack_reserve || !stack_commit) if (!stack_reserve || !stack_commit)
{ {
@ -231,22 +254,13 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
if (stack_reserve < stack_commit) stack_reserve = stack_commit; if (stack_reserve < stack_commit) stack_reserve = stack_commit;
stack_reserve = (stack_reserve + 0xffff) & ~0xffff; /* round to 64K boundary */ stack_reserve = (stack_reserve + 0xffff) & ~0xffff; /* round to 64K boundary */
status = NtAllocateVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, NULL, info->stack_base = NULL;
&stack_reserve, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); info->stack_size = stack_reserve;
if (status != STATUS_SUCCESS) goto error; info->entry = start_thread;
/* limit is lower than base since the stack grows down */ if (wine_pthread_create_thread( info ) == -1)
teb->Tib.StackBase = (char *)teb->DeallocationStack + stack_reserve;
teb->Tib.StackLimit = teb->DeallocationStack;
/* setup the guard page */
size = 1;
NtProtectVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size,
PAGE_EXECUTE_READWRITE | PAGE_GUARD, NULL );
if (SYSDEPS_SpawnThread( start_thread, teb ) == -1)
{ {
status = STATUS_TOO_MANY_THREADS; status = STATUS_NO_MEMORY;
goto error; goto error;
} }
@ -258,6 +272,7 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
error: error:
if (teb) free_teb( teb ); if (teb) free_teb( teb );
if (info) RtlFreeHeap( GetProcessHeap(), 0, info );
if (handle) NtClose( handle ); if (handle) NtClose( handle );
close( request_pipe[1] ); close( request_pipe[1] );
return status; return status;

View File

@ -21,6 +21,10 @@
#ifndef __WINE_WINE_PTHREAD_H #ifndef __WINE_WINE_PTHREAD_H
#define __WINE_WINE_PTHREAD_H #define __WINE_WINE_PTHREAD_H
struct wine_pthread_functions;
#ifdef HAVE_PTHREAD_H
#define _GNU_SOURCE #define _GNU_SOURCE
#include <pthread.h> #include <pthread.h>
@ -65,7 +69,35 @@ struct wine_pthread_functions
const struct timespec *abstime); const struct timespec *abstime);
}; };
#endif /* HAVE_PTHREAD_H */
/* we don't want to include winnt.h here */
#ifndef DECLSPEC_NORETURN
# if defined(_MSC_VER) && (_MSC_VER >= 1200)
# define DECLSPEC_NORETURN __declspec(noreturn)
# elif defined(__GNUC__)
# define DECLSPEC_NORETURN __attribute__((noreturn))
# else
# define DECLSPEC_NORETURN
# endif
#endif
/* thread information used to creating and exiting threads */
struct wine_pthread_thread_info
{
void *stack_base;
size_t stack_size;
void *teb_base;
size_t teb_size;
unsigned short teb_sel;
void (*entry)( struct wine_pthread_thread_info *info );
int exit_status;
};
extern void wine_pthread_init_process( const struct wine_pthread_functions *functions ); extern void wine_pthread_init_process( const struct wine_pthread_functions *functions );
extern void wine_pthread_init_thread(void); extern void wine_pthread_init_thread(void);
extern int wine_pthread_create_thread( struct wine_pthread_thread_info *info );
extern void DECLSPEC_NORETURN wine_pthread_exit_thread( struct wine_pthread_thread_info *info );
extern void DECLSPEC_NORETURN wine_pthread_abort_thread( int status );
#endif /* __WINE_WINE_PTHREAD_H */ #endif /* __WINE_WINE_PTHREAD_H */

View File

@ -38,33 +38,49 @@
#endif #endif
#include "wine/library.h" #include "wine/library.h"
#ifdef HAVE_PTHREAD_H
#include "wine/pthread.h" #include "wine/pthread.h"
/* Note: the wine_pthread functions are just placeholders,
* they will be overridden by the pthread support code.
*/
/*********************************************************************** /***********************************************************************
* wine_pthread_init_process * wine_pthread_init_process
*
* This function is just a placeholder, it will be overridden by the pthread support code.
*/ */
void wine_pthread_init_process( const struct wine_pthread_functions *functions ) void wine_pthread_init_process( const struct wine_pthread_functions *functions )
{ {
assert(0); /* we must never get here */
} }
/*********************************************************************** /***********************************************************************
* wine_pthread_init_thread * wine_pthread_init_thread
*
* This function is just a placeholder, it will be overridden by the pthread support code.
*/ */
void wine_pthread_init_thread(void) void wine_pthread_init_thread(void)
{ {
assert(0); /* we must never get here */
} }
#endif /* HAVE_PTHREAD_H */ /***********************************************************************
* wine_pthread_create_thread
*/
int wine_pthread_create_thread( struct wine_pthread_thread_info *info )
{
return -1;
}
/***********************************************************************
* wine_pthread_exit_thread
*/
void wine_pthread_exit_thread( struct wine_pthread_thread_info *info )
{
exit( info->exit_status );
}
/***********************************************************************
* wine_pthread_abort_thread
*/
void wine_pthread_abort_thread( int status )
{
exit( status );
}
/*********************************************************************** /***********************************************************************

View File

@ -25,8 +25,6 @@
#include "config.h" #include "config.h"
#include "wine/port.h" #include "wine/port.h"
#ifndef HAVE_NPTL
struct _pthread_cleanup_buffer; struct _pthread_cleanup_buffer;
#include <assert.h> #include <assert.h>
@ -58,8 +56,11 @@ struct _pthread_cleanup_buffer;
#include <valgrind/memcheck.h> #include <valgrind/memcheck.h>
#endif #endif
#include "wine/library.h"
#include "wine/pthread.h" #include "wine/pthread.h"
#ifndef HAVE_NPTL
#define P_OUTPUT(stuff) write(2,stuff,strlen(stuff)) #define P_OUTPUT(stuff) write(2,stuff,strlen(stuff))
#define PSTR(str) __ASM_NAME(#str) #define PSTR(str) __ASM_NAME(#str)
@ -151,6 +152,43 @@ static inline void writejump( const char *symbol, void *dest )
#endif /* __GLIBC__ && __i386__ */ #endif /* __GLIBC__ && __i386__ */
} }
/* temporary stacks used on thread exit */
#define TEMP_STACK_SIZE 1024
#define NB_TEMP_STACKS 8
static char temp_stacks[NB_TEMP_STACKS][TEMP_STACK_SIZE];
static LONG next_temp_stack; /* next temp stack to use */
/***********************************************************************
* get_temp_stack
*
* Get a temporary stack address to run the thread exit code on.
*/
inline static char *get_temp_stack(void)
{
unsigned int next = interlocked_xchg_add( &next_temp_stack, 1 );
return temp_stacks[next % NB_TEMP_STACKS] + TEMP_STACK_SIZE;
}
/***********************************************************************
* cleanup_thread
*
* Cleanup the remains of a thread. Runs on a temporary stack.
*/
static void cleanup_thread( void *ptr )
{
/* copy the info structure since it is on the stack we will free */
struct wine_pthread_thread_info info = *(struct wine_pthread_thread_info *)ptr;
wine_ldt_free_fs( info.teb_sel );
munmap( info.stack_base, info.stack_size );
munmap( info.teb_base, info.teb_size );
#ifdef HAVE__LWP_CREATE
_lwp_exit();
#endif
_exit( info.exit_status );
}
/*********************************************************************** /***********************************************************************
* wine_pthread_init_process * wine_pthread_init_process
* *
@ -191,6 +229,78 @@ void wine_pthread_init_thread(void)
} }
/***********************************************************************
* wine_pthread_create_thread
*/
int wine_pthread_create_thread( struct wine_pthread_thread_info *info )
{
if (!info->stack_base)
{
info->stack_base = wine_anon_mmap( NULL, info->stack_size,
PROT_READ | PROT_WRITE | PROT_EXEC, 0 );
if (info->stack_base == (void *)-1) return -1;
}
#ifdef HAVE_CLONE
if (clone( (int (*)(void *))info->entry, (char *)info->stack_base + info->stack_size,
CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, info ) < 0)
return -1;
return 0;
#elif defined(HAVE_RFORK)
{
void **sp = (void **)((char *)info->stack_base + info->stack_size);
*--sp = info;
*--sp = 0;
*--sp = info->entry;
__asm__ __volatile__(
"pushl %2;\n\t" /* flags */
"pushl $0;\n\t" /* 0 ? */
"movl %1,%%eax;\n\t" /* SYS_rfork */
".byte 0x9a; .long 0; .word 7;\n\t" /* lcall 7:0... FreeBSD syscall */
"cmpl $0, %%edx;\n\t"
"je 1f;\n\t"
"movl %0,%%esp;\n\t" /* child -> new thread */
"ret;\n"
"1:\n\t" /* parent -> caller thread */
"addl $8,%%esp" :
: "r" (sp), "g" (SYS_rfork), "g" (RFPROC | RFMEM)
: "eax", "edx");
return 0;
}
#elif defined(HAVE__LWP_CREATE)
{
ucontext_t context;
_lwp_makecontext( &context, (void(*)(void *))info->entry, info,
NULL, info->stack_base, info->stack_size );
if ( _lwp_create( &context, 0, NULL ) )
return -1;
return 0;
}
#endif
return -1;
}
/***********************************************************************
* wine_pthread_exit_thread
*/
void wine_pthread_exit_thread( struct wine_pthread_thread_info *info )
{
wine_switch_to_stack( cleanup_thread, info, get_temp_stack() );
}
/***********************************************************************
* wine_pthread_abort_thread
*/
void wine_pthread_abort_thread( int status )
{
#ifdef HAVE__LWP_CREATE
_lwp_exit();
#endif
_exit( status );
}
/* Currently this probably works only for glibc2, /* Currently this probably works only for glibc2,
* which checks for the presence of double-underscore-prepended * which checks for the presence of double-underscore-prepended
* pthread primitives, and use them if available. * pthread primitives, and use them if available.
@ -825,12 +935,84 @@ static struct pthread_functions libc_pthread_functions =
#else /* HAVE_NPTL */ #else /* HAVE_NPTL */
/***********************************************************************
* wine_pthread_init_process
*
* Initialization for a newly created process.
*/
void wine_pthread_init_process( const struct wine_pthread_functions *functions ) void wine_pthread_init_process( const struct wine_pthread_functions *functions )
{ {
} }
/***********************************************************************
* wine_pthread_init_thread
*
* Initialization for a newly created thread.
*/
void wine_pthread_init_thread(void) void wine_pthread_init_thread(void)
{ {
} }
/***********************************************************************
* wine_pthread_create_thread
*/
int wine_pthread_create_thread( struct wine_pthread_thread_info *info )
{
pthread_t id;
pthread_attr_t attr;
if (!info->stack_base)
{
info->stack_base = wine_anon_mmap( NULL, info->stack_size,
PROT_READ | PROT_WRITE | PROT_EXEC, 0 );
if (info->stack_base == (void *)-1) return -1;
}
pthread_attr_init( &attr );
pthread_attr_setstack( &attr, info->stack_base, info->stack_size );
if (pthread_create( &id, &attr, (void * (*)(void *))info->entry, info )) return -1;
return 0;
}
/***********************************************************************
* wine_pthread_exit_thread
*/
void wine_pthread_exit_thread( struct wine_pthread_thread_info *info )
{
struct cleanup_info
{
pthread_t self;
struct wine_pthread_thread_info thread_info;
};
static struct cleanup_info *previous_info;
struct cleanup_info *cleanup_info, *free_info;
void *ptr;
/* store it at the end of the TEB structure */
cleanup_info = (struct cleanup_info *)((char *)info->teb_base + info->teb_size) - 1;
cleanup_info->self = pthread_self();
cleanup_info->thread_info = *info;
if ((free_info = interlocked_xchg_ptr( (void **)&previous_info, cleanup_info )) != NULL)
{
pthread_join( free_info->self, &ptr );
wine_ldt_free_fs( free_info->thread_info.teb_sel );
munmap( free_info->thread_info.stack_base, free_info->thread_info.stack_size );
munmap( free_info->thread_info.teb_base, free_info->thread_info.teb_size );
}
pthread_exit( (void *)info->exit_status );
}
/***********************************************************************
* wine_pthread_abort_thread
*/
void wine_pthread_abort_thread( int status )
{
pthread_exit( (void *)status );
}
#endif /* HAVE_NPTL */ #endif /* HAVE_NPTL */