Added support for cleaning up the TEB from inside the exiting thread.
This commit is contained in:
parent
c77c4df35e
commit
5016e921f5
|
@ -89,8 +89,7 @@ typedef struct _TEB
|
|||
DWORD unknown6[5]; /* --n 1e8 Unknown */
|
||||
|
||||
/* The following are Wine-specific fields (NT: GDI stuff) */
|
||||
DWORD cleanup; /* --3 1fc Cleanup service handle */
|
||||
DWORD unused[3]; /* --3 200 Was server buffer */
|
||||
DWORD unused[4]; /* --3 1fc Was server buffer */
|
||||
int request_fd; /* --3 20c fd for sending server requests */
|
||||
int reply_fd; /* --3 210 fd for receiving server replies */
|
||||
int wait_fd[2]; /* --3 214 fd for sleeping server requests */
|
||||
|
@ -132,5 +131,11 @@ extern TEB *THREAD_IdToTEB( DWORD id );
|
|||
extern int SYSDEPS_SpawnThread( TEB *teb );
|
||||
extern void SYSDEPS_SetCurThread( TEB *teb );
|
||||
extern void SYSDEPS_ExitThread( int status ) WINE_NORETURN;
|
||||
extern void SYSDEPS_AbortThread( int status ) WINE_NORETURN;
|
||||
extern void SYSDEPS_SwitchToThreadStack( void (*func)(void) ) WINE_NORETURN;
|
||||
|
||||
/* signal handling */
|
||||
extern BOOL SIGNAL_Init(void);
|
||||
extern void SIGNAL_Reset(void);
|
||||
|
||||
#endif /* __WINE_THREAD_H */
|
||||
|
|
|
@ -179,7 +179,6 @@ extern void __wine_enter_vm86( CONTEXT *context );
|
|||
|
||||
#ifdef __WINE__
|
||||
extern void WINAPI EXC_RtlRaiseException( PEXCEPTION_RECORD, PCONTEXT );
|
||||
extern BOOL SIGNAL_Init(void);
|
||||
#endif
|
||||
|
||||
#endif /* __WINE_WINE_EXCEPTION_H */
|
||||
|
|
|
@ -27,6 +27,8 @@ inline static WORD get_sel_count( WORD sel )
|
|||
return (wine_ldt_copy.limit[sel >> __AHSHIFT] >> 16) + 1;
|
||||
}
|
||||
|
||||
static const LDT_ENTRY null_entry; /* all-zeros, used to clear LDT entries */
|
||||
|
||||
/***********************************************************************
|
||||
* SELECTOR_AllocArray
|
||||
*
|
||||
|
@ -98,8 +100,6 @@ WORD WINAPI AllocSelector16( WORD sel )
|
|||
*/
|
||||
WORD WINAPI FreeSelector16( WORD sel )
|
||||
{
|
||||
LDT_ENTRY entry;
|
||||
|
||||
if (IS_SELECTOR_FREE(sel)) return sel; /* error */
|
||||
|
||||
#ifdef __i386__
|
||||
|
@ -112,13 +112,29 @@ WORD WINAPI FreeSelector16( WORD sel )
|
|||
if (!((__get_gs() ^ sel) & ~7)) __set_gs( 0 );
|
||||
#endif /* __i386__ */
|
||||
|
||||
memset( &entry, 0, sizeof(entry) ); /* clear the LDT entries */
|
||||
wine_ldt_set_entry( sel, &entry );
|
||||
wine_ldt_set_entry( sel, &null_entry );
|
||||
wine_ldt_copy.flags[sel >> __AHSHIFT] &= ~WINE_LDT_FLAGS_ALLOCATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SELECTOR_FreeFs
|
||||
*
|
||||
* Free the current %fs selector.
|
||||
*/
|
||||
void SELECTOR_FreeFs(void)
|
||||
{
|
||||
WORD fs = __get_fs();
|
||||
if (fs)
|
||||
{
|
||||
wine_ldt_copy.flags[fs >> __AHSHIFT] &= ~WINE_LDT_FLAGS_ALLOCATED;
|
||||
__set_fs(0);
|
||||
wine_ldt_set_entry( fs, &null_entry );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SELECTOR_SetEntries
|
||||
*
|
||||
|
|
|
@ -958,7 +958,13 @@ BOOL WINAPI VirtualFree(
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* Compute the protection flags */
|
||||
/* Check the type */
|
||||
|
||||
if (type & MEM_SYSTEM)
|
||||
{
|
||||
view->flags |= VFLAG_SYSTEM;
|
||||
type &= ~MEM_SYSTEM;
|
||||
}
|
||||
|
||||
if ((type != MEM_DECOMMIT) && (type != MEM_RELEASE))
|
||||
{
|
||||
|
|
|
@ -98,7 +98,7 @@ void server_protocol_error( const char *err, ... )
|
|||
fprintf( stderr, "wine client error:%p: ", NtCurrentTeb()->tid );
|
||||
vfprintf( stderr, err, args );
|
||||
va_end( args );
|
||||
SYSDEPS_ExitThread(1);
|
||||
SYSDEPS_AbortThread(1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -109,7 +109,7 @@ void server_protocol_perror( const char *err )
|
|||
{
|
||||
fprintf( stderr, "wine client error:%p: ", NtCurrentTeb()->tid );
|
||||
perror( err );
|
||||
SYSDEPS_ExitThread(1);
|
||||
SYSDEPS_AbortThread(1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -144,7 +144,7 @@ static void send_request( const struct __server_request_info *req )
|
|||
}
|
||||
|
||||
if (ret >= 0) server_protocol_error( "partial write %d\n", ret );
|
||||
if (errno == EPIPE) SYSDEPS_ExitThread(0);
|
||||
if (errno == EPIPE) SYSDEPS_AbortThread(0);
|
||||
server_protocol_perror( "sendmsg" );
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ static void read_reply_data( void *buffer, size_t size )
|
|||
server_protocol_perror("read");
|
||||
}
|
||||
/* the server closed the connection; time to die... */
|
||||
SYSDEPS_ExitThread(0);
|
||||
SYSDEPS_AbortThread(0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -252,7 +252,7 @@ void wine_server_send_fd( int fd )
|
|||
if ((ret = sendmsg( fd_socket, &msghdr, 0 )) == sizeof(data)) return;
|
||||
if (ret >= 0) server_protocol_error( "partial write %d\n", ret );
|
||||
if (errno == EINTR) continue;
|
||||
if (errno == EPIPE) SYSDEPS_ExitThread(0);
|
||||
if (errno == EPIPE) SYSDEPS_AbortThread(0);
|
||||
server_protocol_perror( "sendmsg" );
|
||||
}
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ static int receive_fd( handle_t *handle )
|
|||
server_protocol_perror("recvmsg");
|
||||
}
|
||||
/* the server closed the connection; time to die... */
|
||||
SYSDEPS_ExitThread(0);
|
||||
SYSDEPS_AbortThread(0);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -109,9 +109,6 @@ extern STARTUPINFOA current_startupinfo;
|
|||
/* scheduler/pthread.c */
|
||||
extern void PTHREAD_init_done(void);
|
||||
|
||||
/* scheduler/sysdeps.c */
|
||||
extern void SYSDEPS_SwitchToThreadStack( void (*func)(void) ) WINE_NORETURN;
|
||||
|
||||
extern BOOL MAIN_MainInit(void);
|
||||
|
||||
typedef WORD WINAPI (*pUserSignalProc)( UINT, DWORD, DWORD, HMODULE16 );
|
||||
|
|
|
@ -140,7 +140,7 @@ static int wait_reply( void *cookie )
|
|||
server_protocol_perror("wakeup read");
|
||||
}
|
||||
/* the server closed the connection; time to die... */
|
||||
SYSDEPS_ExitThread(0);
|
||||
SYSDEPS_AbortThread(0);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,9 +21,13 @@
|
|||
#ifdef HAVE_UCONTEXT_H
|
||||
# include <ucontext.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include "thread.h"
|
||||
#include "wine/server.h"
|
||||
#include "winbase.h"
|
||||
#include "wine/winbase16.h"
|
||||
#include "wine/exception.h"
|
||||
#include "debugtools.h"
|
||||
|
||||
|
@ -42,6 +46,21 @@ DEFAULT_DEBUG_CHANNEL(thread);
|
|||
# endif /* CLONE_VM */
|
||||
#endif /* linux || HAVE_CLONE */
|
||||
|
||||
extern void SELECTOR_FreeFs(void);
|
||||
|
||||
struct thread_cleanup_info
|
||||
{
|
||||
void *stack_base;
|
||||
int stack_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
|
||||
*
|
||||
|
@ -58,6 +77,58 @@ void SYSDEPS_SetCurThread( TEB *teb )
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* call_on_thread_stack
|
||||
*
|
||||
* Call a function once we switched to the thread stack.
|
||||
*/
|
||||
static void call_on_thread_stack( void *func )
|
||||
{
|
||||
__TRY
|
||||
{
|
||||
void (*funcptr)(void) = func;
|
||||
funcptr();
|
||||
}
|
||||
__EXCEPT(UnhandledExceptionFilter)
|
||||
{
|
||||
TerminateThread( GetCurrentThread(), GetExceptionCode() );
|
||||
}
|
||||
__ENDTRY
|
||||
SYSDEPS_ExitThread(0); /* should never get here */
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* 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 = InterlockedExchangeAdd( &next_temp_stack, 1 );
|
||||
return temp_stacks[next % NB_TEMP_STACKS];
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* 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 );
|
||||
SELECTOR_FreeFs();
|
||||
#ifdef HAVE__LWP_CREATE
|
||||
_lwp_exit();
|
||||
#endif
|
||||
_exit( info.status );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SYSDEPS_StartThread
|
||||
*
|
||||
|
@ -138,6 +209,36 @@ int SYSDEPS_SpawnThread( TEB *teb )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SYSDEPS_CallOnStack
|
||||
*/
|
||||
void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg ) WINE_NORETURN;
|
||||
#ifdef __i386__
|
||||
__ASM_GLOBAL_FUNC( SYSDEPS_CallOnStack,
|
||||
"movl 4(%esp),%ecx\n\t" /* func */
|
||||
"movl 8(%esp),%edx\n\t" /* arg */
|
||||
".byte 0x64\n\tmovl 0x04,%esp\n\t" /* teb->stack_top */
|
||||
"pushl %edx\n\t"
|
||||
"xorl %ebp,%ebp\n\t"
|
||||
"call *%ecx\n\t"
|
||||
"int $3" /* we never return here */ );
|
||||
#else
|
||||
void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg )
|
||||
{
|
||||
func( arg );
|
||||
while(1); /* avoid warning */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SYSDEPS_SwitchToThreadStack
|
||||
*/
|
||||
void SYSDEPS_SwitchToThreadStack( void (*func)(void) )
|
||||
{
|
||||
SYSDEPS_CallOnStack( call_on_thread_stack, func );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SYSDEPS_ExitThread
|
||||
|
@ -146,97 +247,49 @@ int SYSDEPS_SpawnThread( TEB *teb )
|
|||
*/
|
||||
void SYSDEPS_ExitThread( int status )
|
||||
{
|
||||
int fd = NtCurrentTeb()->request_fd;
|
||||
NtCurrentTeb()->request_fd = -1;
|
||||
close( fd );
|
||||
TEB *teb = NtCurrentTeb();
|
||||
struct thread_cleanup_info info;
|
||||
MEMORY_BASIC_INFORMATION meminfo;
|
||||
|
||||
FreeSelector16( teb->stack_sel );
|
||||
VirtualQuery( teb->stack_top, &meminfo, sizeof(meminfo) );
|
||||
info.stack_base = meminfo.AllocationBase;
|
||||
info.stack_size = meminfo.RegionSize + ((char *)teb->stack_top - (char *)meminfo.AllocationBase);
|
||||
info.status = status;
|
||||
|
||||
SIGNAL_Reset();
|
||||
|
||||
VirtualFree( teb->stack_base, 0, MEM_RELEASE | MEM_SYSTEM );
|
||||
close( teb->wait_fd[0] );
|
||||
close( teb->wait_fd[1] );
|
||||
close( teb->reply_fd );
|
||||
close( teb->request_fd );
|
||||
teb->stack_low = get_temp_stack();
|
||||
teb->stack_top = teb->stack_low + TEMP_STACK_SIZE;
|
||||
SYSDEPS_CallOnStack( cleanup_thread, &info );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SYSDEPS_AbortThread
|
||||
*
|
||||
* Same as SYSDEPS_ExitThread, but must not do anything that requires a server call.
|
||||
*/
|
||||
void SYSDEPS_AbortThread( int status )
|
||||
{
|
||||
SIGNAL_Reset();
|
||||
close( NtCurrentTeb()->wait_fd[0] );
|
||||
close( NtCurrentTeb()->wait_fd[1] );
|
||||
close( NtCurrentTeb()->reply_fd );
|
||||
close( NtCurrentTeb()->request_fd );
|
||||
#ifdef HAVE__LWP_CREATE
|
||||
_lwp_exit();
|
||||
#endif
|
||||
_exit( status );
|
||||
/*
|
||||
* It is of course impossible to come here,
|
||||
* but it eliminates a compiler warning.
|
||||
*/
|
||||
exit( status );
|
||||
for (;;) /* avoid warning */
|
||||
_exit( status );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SYSDEPS_CallOnStack
|
||||
*/
|
||||
int SYSDEPS_DoCallOnStack( int (*func)(LPVOID), LPVOID arg )
|
||||
{
|
||||
int retv = 0;
|
||||
|
||||
__TRY
|
||||
{
|
||||
retv = func( arg );
|
||||
}
|
||||
__EXCEPT(UnhandledExceptionFilter)
|
||||
{
|
||||
TerminateThread( GetCurrentThread(), GetExceptionCode() );
|
||||
return 0;
|
||||
}
|
||||
__ENDTRY
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
#ifdef __i386__
|
||||
int SYSDEPS_CallOnStack( LPVOID stackTop, LPVOID stackLow,
|
||||
int (*func)(LPVOID), LPVOID arg );
|
||||
__ASM_GLOBAL_FUNC( SYSDEPS_CallOnStack,
|
||||
"pushl %ebp\n\t"
|
||||
"movl %esp, %ebp\n\t"
|
||||
".byte 0x64; pushl 0x04\n\t"
|
||||
".byte 0x64; pushl 0x08\n\t"
|
||||
"movl 8(%ebp), %esp\n\t"
|
||||
"movl 12(%ebp), %eax\n\t"
|
||||
".byte 0x64; movl %esp, 0x04\n\t"
|
||||
".byte 0x64; movl %eax, 0x08\n\t"
|
||||
"pushl 20(%ebp)\n\t"
|
||||
"pushl 16(%ebp)\n\t"
|
||||
"call " __ASM_NAME("SYSDEPS_DoCallOnStack") "\n\t"
|
||||
"leal -8(%ebp), %esp\n\t"
|
||||
".byte 0x64; popl 0x08\n\t"
|
||||
".byte 0x64; popl 0x04\n\t"
|
||||
"popl %ebp\n\t"
|
||||
"ret" );
|
||||
#else
|
||||
int SYSDEPS_CallOnStack( LPVOID stackTop, LPVOID stackLow,
|
||||
int (*func)(LPVOID), LPVOID arg )
|
||||
{
|
||||
return SYSDEPS_DoCallOnStack( func, arg );
|
||||
}
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
* SYSDEPS_SwitchToThreadStack
|
||||
*/
|
||||
void SYSDEPS_SwitchToThreadStack( void (*func)(void) )
|
||||
{
|
||||
DWORD page_size = getpagesize();
|
||||
void *cur_stack = (void *)(((ULONG_PTR)&func + (page_size-1)) & ~(page_size-1));
|
||||
|
||||
TEB *teb = NtCurrentTeb();
|
||||
LPVOID stackTop = teb->stack_top;
|
||||
LPVOID stackLow = teb->stack_low;
|
||||
|
||||
struct rlimit rl;
|
||||
|
||||
if ( getrlimit(RLIMIT_STACK, &rl) < 0 )
|
||||
{
|
||||
WARN("Can't get rlimit\n");
|
||||
rl.rlim_cur = 8*1024*1024;
|
||||
}
|
||||
|
||||
teb->stack_top = cur_stack;
|
||||
teb->stack_low = (char *)cur_stack - rl.rlim_cur;
|
||||
|
||||
SYSDEPS_CallOnStack( stackTop, stackLow,
|
||||
(int (*)(void *))func, NULL );
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* NtCurrentTeb (NTDLL.@)
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue