From 8574412e1e72bc71d74d6cd7c39768d02ed94169 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 5 Nov 2003 23:31:11 +0000 Subject: [PATCH] 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. --- dlls/kernel/thread.c | 38 +++++++++- dlls/ntdll/Makefile.in | 2 +- dlls/ntdll/ntdll.spec | 1 - dlls/ntdll/sysdeps.c | 166 +------------------------------------------ dlls/ntdll/thread.c | 63 ++++++++++------- include/wine/pthread.h | 32 +++++++++ libs/wine/port.c | 38 +++++++--- scheduler/pthread.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 321 insertions(+), 205 deletions(-) diff --git a/dlls/kernel/thread.c b/dlls/kernel/thread.c index 8fb3ed8b754..0018bf644c9 100644 --- a/dlls/kernel/thread.c +++ b/dlls/kernel/thread.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #ifdef HAVE_SYS_TIMES_H #include @@ -41,6 +42,7 @@ #include "thread.h" #include "wine/winbase16.h" #include "wine/library.h" +#include "wine/pthread.h" #include "wine/server.h" #include "wine/debug.h" @@ -197,11 +199,45 @@ void WINAPI ExitThread( DWORD code ) /* [in] Exit code for this thread */ } else { + struct wine_pthread_thread_info info; + sigset_t block_set; + ULONG size; + LdrShutdownThread(); RtlAcquirePebLock(); RemoveEntryList( &NtCurrentTeb()->TlsLinks ); 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 ); } } diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index 6cc6793d0ab..3b797478f1d 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -4,7 +4,7 @@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = ntdll.dll -EXTRALIBS = $(LIBUNICODE) @LIBPTHREAD@ +EXTRALIBS = $(LIBUNICODE) C_SRCS = \ cdrom.c \ diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index cdc5eb2347f..72a48e3ba78 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1082,6 +1082,5 @@ @ cdecl MODULE_DllThreadAttach(ptr) @ cdecl MODULE_GetLoadOrderA(ptr str str long) @ cdecl MODULE_GetLoadOrderW(ptr wstr wstr long) -@ cdecl SYSDEPS_ExitThread(long) @ cdecl VERSION_Init(wstr) @ cdecl VIRTUAL_SetFaultHandler(ptr ptr ptr) diff --git a/dlls/ntdll/sysdeps.c b/dlls/ntdll/sysdeps.c index 4130dedadf8..4bb0830e544 100644 --- a/dlls/ntdll/sysdeps.c +++ b/dlls/ntdll/sysdeps.c @@ -55,21 +55,6 @@ 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 @@ -97,148 +82,7 @@ void SYSDEPS_SetCurThread( TEB *teb ) /* On non-i386 Solaris, we use the LWP private pointer */ _lwp_setprivate( teb ); #endif - -#ifdef HAVE_NPTL - teb->pthread_data = (void *)pthread_self(); -#else 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()->reply_fd ); close( NtCurrentTeb()->request_fd ); -#ifdef HAVE_NPTL - pthread_exit( (void *)status ); -#endif - SIGNAL_Reset(); -#ifdef HAVE__LWP_CREATE - _lwp_exit(); -#endif - for (;;) /* avoid warning */ - _exit( status ); + wine_pthread_abort_thread( status ); } /*********************************************************************** diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 9a13a943d5c..f64b1f3525f 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -31,6 +31,7 @@ #include "winternl.h" #include "wine/library.h" #include "wine/server.h" +#include "wine/pthread.h" #include "wine/debug.h" #include "ntdll_misc.h" @@ -60,6 +61,7 @@ static TEB *alloc_teb( ULONG *size ) return NULL; } teb->Tib.ExceptionList = (void *)~0UL; + teb->Tib.StackBase = (void *)~0UL; teb->Tib.Self = &teb->Tib; teb->Peb = &peb; teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; @@ -76,8 +78,6 @@ static inline void free_teb( TEB *teb ) ULONG size = 0; void *addr = teb; - if (teb->DeallocationStack) - NtFreeVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size, MEM_RELEASE ); NtFreeVirtualMemory( GetCurrentProcess(), &addr, &size, MEM_RELEASE ); wine_ldt_free_fs( teb->teb_sel ); munmap( teb, SIGNAL_STACK_SIZE + sizeof(TEB) ); @@ -110,7 +110,6 @@ void thread_init(void) InitializeListHead( &tls_links ); teb = alloc_teb( &size ); - teb->Tib.StackBase = (void *)~0UL; teb->tibflags = TEBF_WIN32; teb->request_fd = -1; teb->reply_fd = -1; @@ -143,19 +142,35 @@ void thread_init(void) * * 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; - struct debug_info info; + struct debug_info debug_info; + ULONG size; - info.str_pos = info.strings; - info.out_pos = info.output; - teb->debug_info = &info; + debug_info.str_pos = debug_info.strings; + debug_info.out_pos = debug_info.output; + teb->debug_info = &debug_info; SYSDEPS_SetCurThread( teb ); SIGNAL_Init(); 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(); InsertHeadList( &tls_links, &teb->TlsLinks ); RtlReleasePebLock(); @@ -173,11 +188,11 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * PRTL_THREAD_START_ROUTINE start, void *param, HANDLE *handle_ptr, CLIENT_ID *id ) { + struct wine_pthread_thread_info *info = NULL; HANDLE handle = 0; TEB *teb = NULL; DWORD tid = 0; ULONG size; - void *base; int request_pipe[2]; NTSTATUS status; @@ -201,6 +216,12 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * if (status) goto error; + if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) ))) + { + status = STATUS_NO_MEMORY; + goto error; + } + if (!(teb = alloc_teb( &size ))) { status = STATUS_NO_MEMORY; @@ -219,8 +240,10 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * teb->entry_arg = param; teb->htask16 = NtCurrentTeb()->htask16; - NtAllocateVirtualMemory( GetCurrentProcess(), &base, teb, &size, + NtAllocateVirtualMemory( GetCurrentProcess(), &info->teb_base, teb, &size, MEM_SYSTEM, PAGE_EXECUTE_READWRITE ); + info->teb_size = size; + info->teb_sel = teb->teb_sel; 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; stack_reserve = (stack_reserve + 0xffff) & ~0xffff; /* round to 64K boundary */ - status = NtAllocateVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, NULL, - &stack_reserve, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - if (status != STATUS_SUCCESS) goto error; + info->stack_base = NULL; + info->stack_size = stack_reserve; + info->entry = start_thread; - /* limit is lower than base since the stack grows down */ - 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) + if (wine_pthread_create_thread( info ) == -1) { - status = STATUS_TOO_MANY_THREADS; + status = STATUS_NO_MEMORY; goto error; } @@ -258,6 +272,7 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * error: if (teb) free_teb( teb ); + if (info) RtlFreeHeap( GetProcessHeap(), 0, info ); if (handle) NtClose( handle ); close( request_pipe[1] ); return status; diff --git a/include/wine/pthread.h b/include/wine/pthread.h index bd78385158b..448c547cb45 100644 --- a/include/wine/pthread.h +++ b/include/wine/pthread.h @@ -21,6 +21,10 @@ #ifndef __WINE_WINE_PTHREAD_H #define __WINE_WINE_PTHREAD_H +struct wine_pthread_functions; + +#ifdef HAVE_PTHREAD_H + #define _GNU_SOURCE #include @@ -65,7 +69,35 @@ struct wine_pthread_functions 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_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 */ diff --git a/libs/wine/port.c b/libs/wine/port.c index d6751fb4b0c..1228e6e1faf 100644 --- a/libs/wine/port.c +++ b/libs/wine/port.c @@ -38,33 +38,49 @@ #endif #include "wine/library.h" - -#ifdef HAVE_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 - * - * 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 ) { - assert(0); /* we must never get here */ } - /*********************************************************************** * 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) { - 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 ); +} /*********************************************************************** diff --git a/scheduler/pthread.c b/scheduler/pthread.c index ab4a3efba95..2baafc5b860 100644 --- a/scheduler/pthread.c +++ b/scheduler/pthread.c @@ -25,8 +25,6 @@ #include "config.h" #include "wine/port.h" -#ifndef HAVE_NPTL - struct _pthread_cleanup_buffer; #include @@ -58,8 +56,11 @@ struct _pthread_cleanup_buffer; #include #endif +#include "wine/library.h" #include "wine/pthread.h" +#ifndef HAVE_NPTL + #define P_OUTPUT(stuff) write(2,stuff,strlen(stuff)) #define PSTR(str) __ASM_NAME(#str) @@ -151,6 +152,43 @@ static inline void writejump( const char *symbol, void *dest ) #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 * @@ -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, * which checks for the presence of double-underscore-prepended * pthread primitives, and use them if available. @@ -825,12 +935,84 @@ static struct pthread_functions libc_pthread_functions = #else /* HAVE_NPTL */ +/*********************************************************************** + * wine_pthread_init_process + * + * Initialization for a newly created process. + */ 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) { } + +/*********************************************************************** + * 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 */ -- 2.11.4.GIT