Have threads and processes exit more cleanly whenever possible.
diff --git a/include/server.h b/include/server.h
index ffb014b..f6031d4 100644
--- a/include/server.h
+++ b/include/server.h
@@ -191,6 +191,7 @@
{
IN int handle; /* process handle to terminate */
IN int exit_code; /* process exit code */
+ OUT int self; /* suicide? */
};
@@ -199,6 +200,8 @@
{
IN int handle; /* thread handle to terminate */
IN int exit_code; /* thread exit code */
+ OUT int self; /* suicide? */
+ OUT int last; /* last thread in this process? */
};
@@ -1202,7 +1205,7 @@
REQ_NB_REQUESTS
};
-#define SERVER_PROTOCOL_VERSION 3
+#define SERVER_PROTOCOL_VERSION 4
/* ### make_requests end ### */
/* Everything above this line is generated automatically by tools/make_requests */
@@ -1219,7 +1222,7 @@
extern unsigned int server_call_noerr( enum request req );
extern unsigned int server_call_fd( enum request req, int fd_out, int *fd_in );
-extern void server_protocol_error( const char *err, ... );
+extern void server_protocol_error( const char *err, ... ) WINE_NORETURN;
/* get a pointer to the request buffer */
static inline void * WINE_UNUSED get_req_buffer(void)
diff --git a/include/thread.h b/include/thread.h
index 88ffd4b..c59aab4 100644
--- a/include/thread.h
+++ b/include/thread.h
@@ -131,6 +131,6 @@
/* scheduler/sysdeps.c */
extern int SYSDEPS_SpawnThread( TEB *teb );
extern void SYSDEPS_SetCurThread( TEB *teb );
-extern void SYSDEPS_ExitThread(void);
+extern void SYSDEPS_ExitThread( int status ) WINE_NORETURN;
#endif /* __WINE_THREAD_H */
diff --git a/include/winbase.h b/include/winbase.h
index 467fa8a..ad89002 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -1223,8 +1223,8 @@
#define EnumTimeFormats WINELIB_NAME_AW(EnumTimeFormats)
BOOL WINAPI EqualSid(PSID, PSID);
BOOL WINAPI EqualPrefixSid(PSID,PSID);
-VOID WINAPI ExitProcess(DWORD);
-VOID WINAPI ExitThread(DWORD);
+VOID WINAPI ExitProcess(DWORD) WINE_NORETURN;
+VOID WINAPI ExitThread(DWORD) WINE_NORETURN;
DWORD WINAPI ExpandEnvironmentStringsA(LPCSTR,LPSTR,DWORD);
DWORD WINAPI ExpandEnvironmentStringsW(LPCWSTR,LPWSTR,DWORD);
#define ExpandEnvironmentStrings WINELIB_NAME_AW(ExpandEnvironmentStrings)
diff --git a/include/windef.h b/include/windef.h
index 6a5c9db..f6a69b4 100644
--- a/include/windef.h
+++ b/include/windef.h
@@ -353,11 +353,13 @@
/* Macro for structure packing. */
#ifdef __GNUC__
-#define WINE_PACKED __attribute__ ((packed))
-#define WINE_UNUSED __attribute__ ((unused))
+#define WINE_PACKED __attribute__((packed))
+#define WINE_UNUSED __attribute__((unused))
+#define WINE_NORETURN __attribute__((noreturn))
#else
-#define WINE_PACKED /* nothing */
-#define WINE_UNUSED /* nothing */
+#define WINE_PACKED /* nothing */
+#define WINE_UNUSED /* nothing */
+#define WINE_NORETURN /* nothing */
#endif
/* Macros to split words and longs. */
diff --git a/loader/module.c b/loader/module.c
index ea528f4..6380919 100644
--- a/loader/module.c
+++ b/loader/module.c
@@ -221,14 +221,13 @@
* Send DLL process detach notifications. See the comment about calling
* sequence at MODULE_DllProcessAttach. Unless the bForceDetach flag
* is set, only DLLs with zero refcount are notified.
- *
- * NOTE: Assumes that the process critical section is held!
- *
*/
void MODULE_DllProcessDetach( BOOL bForceDetach, LPVOID lpReserved )
{
WINE_MODREF *wm;
+ EnterCriticalSection( &PROCESS_Current()->crit_section );
+
do
{
for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
@@ -248,6 +247,8 @@
break;
}
} while ( wm );
+
+ LeaveCriticalSection( &PROCESS_Current()->crit_section );
}
/*************************************************************************
diff --git a/scheduler/client.c b/scheduler/client.c
index 1215725..6767416 100644
--- a/scheduler/client.c
+++ b/scheduler/client.c
@@ -78,17 +78,6 @@
}
/***********************************************************************
- * CLIENT_Die
- *
- * Die on protocol errors or socket close
- */
-static void CLIENT_Die(void)
-{
- close( NtCurrentTeb()->socket );
- SYSDEPS_ExitThread();
-}
-
-/***********************************************************************
* server_protocol_error
*/
void server_protocol_error( const char *err, ... )
@@ -99,7 +88,7 @@
fprintf( stderr, "Client protocol error:%p: ", NtCurrentTeb()->tid );
vfprintf( stderr, err, args );
va_end( args );
- CLIENT_Die();
+ SYSDEPS_ExitThread(1);
}
@@ -110,7 +99,7 @@
{
fprintf( stderr, "Client protocol error:%p: ", NtCurrentTeb()->tid );
perror( err );
- CLIENT_Die();
+ SYSDEPS_ExitThread(1);
}
@@ -126,7 +115,7 @@
return;
if (ret == -1)
{
- if (errno == EPIPE) CLIENT_Die();
+ if (errno == EPIPE) SYSDEPS_ExitThread(0);
server_perror( "sendmsg" );
}
server_protocol_error( "partial msg sent %d/%d\n", ret, sizeof(req) );
@@ -170,7 +159,7 @@
if ((ret = sendmsg( NtCurrentTeb()->socket, &msghdr, 0 )) == sizeof(req)) return;
if (ret == -1)
{
- if (errno == EPIPE) CLIENT_Die();
+ if (errno == EPIPE) SYSDEPS_ExitThread(0);
server_perror( "sendmsg" );
}
server_protocol_error( "partial msg sent %d/%d\n", ret, sizeof(req) );
@@ -190,15 +179,17 @@
{
if ((ret = read( NtCurrentTeb()->socket, &res, sizeof(res) )) == sizeof(res))
return res;
+ if (!ret) break;
if (ret == -1)
{
if (errno == EINTR) continue;
- if (errno == EPIPE) CLIENT_Die();
+ if (errno == EPIPE) break;
server_perror("read");
}
- if (!ret) CLIENT_Die(); /* the server closed the connection; time to die... */
server_protocol_error( "partial msg received %d/%d\n", ret, sizeof(res) );
}
+ /* the server closed the connection; time to die... */
+ SYSDEPS_ExitThread(0);
}
@@ -248,15 +239,17 @@
#endif
return res;
}
+ if (!ret) break;
if (ret == -1)
{
if (errno == EINTR) continue;
- if (errno == EPIPE) CLIENT_Die();
+ if (errno == EPIPE) break;
server_perror("recvmsg");
}
- if (!ret) CLIENT_Die(); /* the server closed the connection; time to die... */
server_protocol_error( "partial seq received %d/%d\n", ret, sizeof(res) );
}
+ /* the server closed the connection; time to die... */
+ SYSDEPS_ExitThread(0);
}
diff --git a/scheduler/process.c b/scheduler/process.c
index ec41917..a783790 100644
--- a/scheduler/process.c
+++ b/scheduler/process.c
@@ -627,12 +627,16 @@
*/
void WINAPI ExitProcess( DWORD status )
{
- EnterCriticalSection( &PROCESS_Current()->crit_section );
- MODULE_DllProcessDetach( TRUE, (LPVOID)1 );
- LeaveCriticalSection( &PROCESS_Current()->crit_section );
+ struct terminate_process_request *req = get_req_buffer();
+ MODULE_DllProcessDetach( TRUE, (LPVOID)1 );
TASK_KillTask( 0 );
- TerminateProcess( GetCurrentProcess(), status );
+
+ /* send the exit code to the server */
+ req->handle = GetCurrentProcess();
+ req->exit_code = status;
+ server_call( REQ_TERMINATE_PROCESS );
+ exit( status );
}
/***********************************************************************
@@ -649,10 +653,12 @@
*/
BOOL WINAPI TerminateProcess( HANDLE handle, DWORD exit_code )
{
+ BOOL ret;
struct terminate_process_request *req = get_req_buffer();
req->handle = handle;
req->exit_code = exit_code;
- return !server_call( REQ_TERMINATE_PROCESS );
+ if ((ret = !server_call( REQ_TERMINATE_PROCESS )) && req->self) exit( exit_code );
+ return ret;
}
diff --git a/scheduler/sysdeps.c b/scheduler/sysdeps.c
index 684357b..559e4a9 100644
--- a/scheduler/sysdeps.c
+++ b/scheduler/sysdeps.c
@@ -137,7 +137,7 @@
TerminateThread( GetCurrentThread(), GetExceptionCode() );
}
__ENDTRY
- SYSDEPS_ExitThread(); /* should never get here */
+ SYSDEPS_ExitThread(0); /* should never get here */
}
@@ -153,31 +153,31 @@
#ifdef linux
if (clone( (int (*)(void *))SYSDEPS_StartThread, teb->stack_top,
- CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, teb ) < 0)
+ CLONE_VM | CLONE_FS | SIGCHLD, teb ) < 0)
return -1;
- /* FIXME: close the child socket in the parent process */
-/* close( thread->socket );*/
+ close( teb->socket ); /* close the child socket in the parent */
return 0;
#endif
#ifdef HAVE_RFORK
- DWORD *sp = (DWORD *)teb->stack_top;
- *--sp = (DWORD)teb;
+ void **sp = (void **)teb->stack_top;
+ *--sp = teb;
*--sp = 0;
- *--sp = (DWORD)SYSDEPS_StartThread;
- __asm__(
- "pushl %2;\n\t" /* RFPROC|RMEM */
+ *--sp = SYSDEPS_StartThread;
+ __asm__ __volatile__(
+ "pushl %2;\n\t" /* RFPROC|RFMEM|RFFDG */
"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" /* father -> new thread */
+ "movl %0,%%esp;\n\t" /* child -> new thread */
"ret;\n"
- "1:\n\t" /* child -> caller thread */
+ "1:\n\t" /* parent -> caller thread */
"addl $8,%%esp" :
- : "r" (sp), "g" (SYS_rfork), "g" (RFPROC|RFMEM)
+ : "r" (sp), "g" (SYS_rfork), "g" (RFPROC|RFMEM|RFFDG)
: "eax", "edx");
+ close( teb->socket ); /* close the child socket in the parent */
return 0;
#endif
@@ -202,15 +202,14 @@
* SYSDEPS_ExitThread
*
* Exit a running thread; must not return.
- * Must not make any reference to the thread structures (THDB etc.) as
- * they have already been deleted.
*/
-void SYSDEPS_ExitThread(void)
+void SYSDEPS_ExitThread( int status )
{
-#if !defined(__i386__) && defined(HAVE__LWP_CREATE)
+#ifdef HAVE__LWP_CREATE
+ close( NtCurrentTeb()->socket );
_lwp_exit();
#endif
- _exit( 0 );
+ _exit( status );
}
diff --git a/scheduler/thread.c b/scheduler/thread.c
index 9082cb8..a0c7391 100644
--- a/scheduler/thread.c
+++ b/scheduler/thread.c
@@ -336,8 +336,23 @@
*/
void WINAPI ExitThread( DWORD code ) /* [in] Exit code for this thread */
{
- MODULE_DllThreadDetach( NULL );
- TerminateThread( GetCurrentThread(), code );
+ struct terminate_thread_request *req = get_req_buffer();
+
+ /* send the exit code to the server */
+ req->handle = GetCurrentThread();
+ req->exit_code = code;
+ server_call( REQ_TERMINATE_THREAD );
+ if (req->last)
+ {
+ MODULE_DllProcessDetach( TRUE, (LPVOID)1 );
+ TASK_KillTask( 0 );
+ exit( code );
+ }
+ else
+ {
+ MODULE_DllThreadDetach( NULL );
+ SYSDEPS_ExitThread( code );
+ }
}
@@ -590,10 +605,16 @@
HANDLE handle, /* [in] Handle to thread */
DWORD exitcode) /* [in] Exit code for thread */
{
+ BOOL ret;
struct terminate_thread_request *req = get_req_buffer();
req->handle = handle;
req->exit_code = exitcode;
- return !server_call( REQ_TERMINATE_THREAD );
+ if ((ret = !server_call( REQ_TERMINATE_THREAD )) && req->self)
+ {
+ if (req->last) exit( exitcode );
+ else SYSDEPS_ExitThread( exitcode );
+ }
+ return ret;
}
diff --git a/server/process.c b/server/process.c
index 8073e55..07b2a0a 100644
--- a/server/process.c
+++ b/server/process.c
@@ -399,10 +399,16 @@
}
/* kill a process on the spot */
-void kill_process( struct process *process, int exit_code )
+static void kill_process( struct process *process, struct thread *skip, int exit_code )
{
- while (process->thread_list)
- kill_thread( process->thread_list, exit_code );
+ struct thread *thread = process->thread_list;
+ while (thread)
+ {
+ struct thread *next = thread->proc_next;
+ thread->exit_code = exit_code;
+ if (thread != skip) kill_thread( thread, 1 );
+ thread = next;
+ }
}
/* kill all processes being debugged by a given thread */
@@ -416,7 +422,7 @@
process = process->next;
if (!process) return;
process->debugger = NULL;
- kill_process( process, exit_code );
+ kill_process( process, NULL, exit_code );
}
}
@@ -678,7 +684,8 @@
if ((process = get_process_from_handle( req->handle, PROCESS_TERMINATE )))
{
- kill_process( process, req->exit_code );
+ req->self = (current->process == process);
+ kill_process( process, current, req->exit_code );
release_object( process );
}
}
diff --git a/server/process.h b/server/process.h
index 9f21e05..6cdbb6a 100644
--- a/server/process.h
+++ b/server/process.h
@@ -73,7 +73,6 @@
struct thread *thread );
extern void suspend_process( struct process *process );
extern void resume_process( struct process *process );
-extern void kill_process( struct process *process, int exit_code );
extern void kill_debugged_processes( struct thread *debugger, int exit_code );
extern struct process_snapshot *process_snap( int *count );
diff --git a/server/ptrace.c b/server/ptrace.c
index 9cd4c04..0346e72 100644
--- a/server/ptrace.c
+++ b/server/ptrace.c
@@ -117,21 +117,21 @@
}
/* detach from a Unix thread and kill it */
-void detach_thread( struct thread *thread )
+void detach_thread( struct thread *thread, int sig )
{
if (!thread->unix_pid) return;
if (thread->attached)
{
/* make sure it is stopped */
if (!(thread->suspend + thread->process->suspend)) stop_thread( thread );
- kill( thread->unix_pid, SIGTERM );
+ if (sig) kill( thread->unix_pid, sig );
if (debug_level) fprintf( stderr, "%08x: *detached*\n", (unsigned int)thread );
- ptrace( PTRACE_DETACH, thread->unix_pid, 1, SIGTERM );
+ ptrace( PTRACE_DETACH, thread->unix_pid, 1, sig );
thread->attached = 0;
}
else
{
- kill( thread->unix_pid, SIGTERM );
+ if (sig) kill( thread->unix_pid, sig );
if (thread->suspend + thread->process->suspend) continue_thread( thread );
}
}
diff --git a/server/request.c b/server/request.c
index 582f839..011124c 100644
--- a/server/request.c
+++ b/server/request.c
@@ -98,7 +98,8 @@
fprintf( stderr, "Protocol error:%p: ", thread );
vfprintf( stderr, err, args );
va_end( args );
- kill_thread( thread, PROTOCOL_ERROR );
+ thread->exit_code = 1;
+ kill_thread( thread, 1 );
}
/* die on a fatal error */
@@ -193,12 +194,13 @@
if (ret == -1)
{
perror("recvmsg");
- kill_thread( thread, BROKEN_PIPE );
+ thread->exit_code = 1;
+ kill_thread( thread, 1 );
return;
}
if (!ret) /* closed pipe */
{
- kill_thread( thread, BROKEN_PIPE );
+ kill_thread( thread, 0 );
return;
}
fatal_protocol_error( thread, "partial message received %d/%d\n", ret, sizeof(req) );
@@ -212,7 +214,6 @@
if (thread->pass_fd == -1)
{
ret = write( thread->obj.fd, &thread->error, sizeof(thread->error) );
- if (ret == sizeof(thread->error)) goto ok;
}
else /* we have an fd to send */
{
@@ -231,20 +232,33 @@
ret = sendmsg( thread->obj.fd, &msghdr, 0 );
close( thread->pass_fd );
thread->pass_fd = -1;
- if (ret == sizeof(thread->error)) goto ok;
+ }
+ if (ret == sizeof(thread->error))
+ {
+ set_select_events( &thread->obj, POLLIN );
+ return 1;
}
if (ret == -1)
{
if (errno == EWOULDBLOCK) return 0; /* not a fatal error */
- if (errno != EPIPE) perror("sendmsg");
+ if (errno == EPIPE)
+ {
+ kill_thread( thread, 0 ); /* normal death */
+ }
+ else
+ {
+ perror("sendmsg");
+ thread->exit_code = 1;
+ kill_thread( thread, 1 );
+ }
}
- else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(thread->error) );
- kill_thread( thread, BROKEN_PIPE );
+ else
+ {
+ thread->exit_code = 1;
+ kill_thread( thread, 1 );
+ fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(thread->error) );
+ }
return -1;
-
- ok:
- set_select_events( &thread->obj, POLLIN );
- return 1;
}
static void master_socket_dump( struct object *obj, int verbose )
diff --git a/server/request.h b/server/request.h
index a907747..3e746db 100644
--- a/server/request.h
+++ b/server/request.h
@@ -16,11 +16,6 @@
/* max request length */
#define MAX_REQUEST_LENGTH 8192
-/* exit code passed to remove_client on communication error */
-#define OUT_OF_MEMORY -1
-#define BROKEN_PIPE -2
-#define PROTOCOL_ERROR -3
-
/* request handler definition */
#define DECL_HANDLER(name) void req_##name( struct name##_request *req )
diff --git a/server/thread.c b/server/thread.c
index e82c4a7..eef39df 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -137,7 +137,7 @@
thread->pass_fd = -1;
thread->state = RUNNING;
thread->attached = 0;
- thread->exit_code = STILL_ACTIVE;
+ thread->exit_code = 0;
thread->next = NULL;
thread->prev = NULL;
thread->priority = THREAD_PRIORITY_NORMAL;
@@ -175,7 +175,7 @@
struct thread *thread = (struct thread *)obj;
assert( obj->ops == &thread_ops );
- if (event & (POLLERR | POLLHUP)) kill_thread( thread, BROKEN_PIPE );
+ if (event & (POLLERR | POLLHUP)) kill_thread( thread, 0 );
else
{
if (event & POLLOUT) write_request( thread );
@@ -556,20 +556,25 @@
}
/* kill a thread on the spot */
-void kill_thread( struct thread *thread, int exit_code )
+void kill_thread( struct thread *thread, int violent_death )
{
if (thread->state == TERMINATED) return; /* already killed */
thread->state = TERMINATED;
- thread->exit_code = exit_code;
if (current == thread) current = NULL;
if (debug_level)
- fprintf( stderr,"%08x: *killed* exit_code=%d\n", (unsigned int)thread, exit_code );
- if (thread->wait) end_wait( thread );
+ fprintf( stderr,"%08x: *killed* exit_code=%d\n",
+ (unsigned int)thread, thread->exit_code );
+ if (thread->wait)
+ {
+ end_wait( thread );
+ /* if it is waiting on the socket, we don't need to send a SIGTERM */
+ violent_death = 0;
+ }
debug_exit_thread( thread );
abandon_mutexes( thread );
remove_process_thread( thread->process, thread );
wake_up( &thread->obj, 0 );
- detach_thread( thread );
+ detach_thread( thread, violent_death ? SIGTERM : 0 );
remove_select_user( &thread->obj );
release_object( thread );
}
@@ -639,9 +644,17 @@
{
struct thread *thread;
+ req->self = 0;
+ req->last = 0;
if ((thread = get_thread_from_handle( req->handle, THREAD_TERMINATE )))
{
- kill_thread( thread, req->exit_code );
+ thread->exit_code = req->exit_code;
+ if (thread != current) kill_thread( thread, 1 );
+ else
+ {
+ req->self = 1;
+ req->last = (thread->process->running_threads == 1);
+ }
release_object( thread );
}
}
@@ -654,7 +667,7 @@
if ((thread = get_thread_from_handle( req->handle, THREAD_QUERY_INFORMATION )))
{
req->tid = thread;
- req->exit_code = thread->exit_code;
+ req->exit_code = (thread->state == TERMINATED) ? thread->exit_code : STILL_ACTIVE;
req->priority = thread->priority;
release_object( thread );
}
diff --git a/server/thread.h b/server/thread.h
index 71baa51..54d38a4 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -74,7 +74,7 @@
extern void resume_all_threads( void );
extern int add_queue( struct object *obj, struct wait_queue_entry *entry );
extern void remove_queue( struct object *obj, struct wait_queue_entry *entry );
-extern void kill_thread( struct thread *thread, int exit_code );
+extern void kill_thread( struct thread *thread, int violent_death );
extern void wake_up( struct object *obj, int max );
extern int sleep_on( int count, struct object *objects[], int flags,
int timeout, sleep_reply func );
@@ -85,7 +85,7 @@
extern void wait4_thread( struct thread *thread, int signal );
extern void stop_thread( struct thread *thread );
extern void continue_thread( struct thread *thread );
-extern void detach_thread( struct thread *thread );
+extern void detach_thread( struct thread *thread, int sig );
extern int suspend_for_ptrace( struct thread *thread );
extern int read_thread_int( struct thread *thread, const int *addr, int *data );
extern int write_thread_int( struct thread *thread, int *addr, int data, unsigned int mask );
diff --git a/server/trace.c b/server/trace.c
index d4ff840..cc1a1ba 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -297,12 +297,23 @@
fprintf( stderr, " exit_code=%d", req->exit_code );
}
+static void dump_terminate_process_reply( const struct terminate_process_request *req )
+{
+ fprintf( stderr, " self=%d", req->self );
+}
+
static void dump_terminate_thread_request( const struct terminate_thread_request *req )
{
fprintf( stderr, " handle=%d,", req->handle );
fprintf( stderr, " exit_code=%d", req->exit_code );
}
+static void dump_terminate_thread_reply( const struct terminate_thread_request *req )
+{
+ fprintf( stderr, " self=%d,", req->self );
+ fprintf( stderr, " last=%d", req->last );
+}
+
static void dump_get_process_info_request( const struct get_process_info_request *req )
{
fprintf( stderr, " handle=%d", req->handle );
@@ -1366,8 +1377,8 @@
(dump_func)dump_init_process_done_reply,
(dump_func)0,
(dump_func)dump_get_thread_buffer_reply,
- (dump_func)0,
- (dump_func)0,
+ (dump_func)dump_terminate_process_reply,
+ (dump_func)dump_terminate_thread_reply,
(dump_func)dump_get_process_info_reply,
(dump_func)0,
(dump_func)dump_get_thread_info_reply,