Commit 5016e921 authored by Alexandre Julliard's avatar Alexandre Julliard

Added support for cleaning up the TEB from inside the exiting thread.

parent c77c4df3
......@@ -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,14 +112,30 @@ 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
*
* Set the LDT entries for an array of selectors.
......
......@@ -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,105 +209,87 @@ int SYSDEPS_SpawnThread( TEB *teb )
}
/***********************************************************************
* SYSDEPS_ExitThread
*
* Exit a running thread; must not return.
*/
void SYSDEPS_ExitThread( int status )
{
int fd = NtCurrentTeb()->request_fd;
NtCurrentTeb()->request_fd = -1;
close( 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 );
}
/***********************************************************************
* 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;
}
void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg ) WINE_NORETURN;
#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" );
"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
int SYSDEPS_CallOnStack( LPVOID stackTop, LPVOID stackLow,
int (*func)(LPVOID), LPVOID arg )
void SYSDEPS_CallOnStack( void (*func)(LPVOID), LPVOID arg )
{
return SYSDEPS_DoCallOnStack( func, arg );
func( arg );
while(1); /* avoid warning */
}
#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));
SYSDEPS_CallOnStack( call_on_thread_stack, func );
}
/***********************************************************************
* SYSDEPS_ExitThread
*
* Exit a running thread; must not return.
*/
void SYSDEPS_ExitThread( int status )
{
TEB *teb = NtCurrentTeb();
LPVOID stackTop = teb->stack_top;
LPVOID stackLow = teb->stack_low;
struct thread_cleanup_info info;
MEMORY_BASIC_INFORMATION meminfo;
struct rlimit rl;
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;
if ( getrlimit(RLIMIT_STACK, &rl) < 0 )
{
WARN("Can't get rlimit\n");
rl.rlim_cur = 8*1024*1024;
}
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 );
}
teb->stack_top = cur_stack;
teb->stack_low = (char *)cur_stack - rl.rlim_cur;
SYSDEPS_CallOnStack( stackTop, stackLow,
(int (*)(void *))func, NULL );
/***********************************************************************
* 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
for (;;) /* avoid warning */
_exit( status );
}
/**********************************************************************
* NtCurrentTeb (NTDLL.@)
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment