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:
parent
8defcd386c
commit
8574412e1e
|
@ -24,6 +24,7 @@
|
|||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_TIMES_H
|
||||
#include <sys/times.h>
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ TOPOBJDIR = ../..
|
|||
SRCDIR = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
MODULE = ntdll.dll
|
||||
EXTRALIBS = $(LIBUNICODE) @LIBPTHREAD@
|
||||
EXTRALIBS = $(LIBUNICODE)
|
||||
|
||||
C_SRCS = \
|
||||
cdrom.c \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <pthread.h>
|
||||
|
||||
|
@ -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 */
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#ifndef HAVE_NPTL
|
||||
|
||||
struct _pthread_cleanup_buffer;
|
||||
|
||||
#include <assert.h>
|
||||
|
@ -58,8 +56,11 @@ struct _pthread_cleanup_buffer;
|
|||
#include <valgrind/memcheck.h>
|
||||
#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 */
|
||||
|
|
Loading…
Reference in New Issue