From: Rose Hellsing <rose@pinkro.se> Add support for sending LPC_CLIENT_DIED messages when a thread terminates: - New lpc_terminate_ports list in thread structure - register_lpc_terminate_port handler to register client ports - lpc_send_client_died function called during thread cleanup to send termination messages to all registered ports --- server/lpc_port.c | 96 +++++++++++++++++++++++++++++++++++++++++++++ server/protocol.def | 6 +++ server/thread.c | 2 + server/thread.h | 5 +++ 4 files changed, 109 insertions(+) diff --git a/server/lpc_port.c b/server/lpc_port.c index 73da2ab648a..ba5e3d7e9da 100644 --- a/server/lpc_port.c +++ b/server/lpc_port.c @@ -54,6 +54,12 @@ #define LPC_REQUEST 1 #define LPC_REPLY 2 #define LPC_DATAGRAM 3 +#define LPC_LOST_REPLY 4 +#define LPC_PORT_CLOSED 5 +#define LPC_CLIENT_DIED 6 +#define LPC_EXCEPTION 7 +#define LPC_DEBUG_EVENT 8 +#define LPC_ERROR_EVENT 9 #define LPC_CONNECTION_REQUEST 10 /* Maximum message size */ @@ -77,6 +83,13 @@ struct pending_request struct lpc_port *client_port; /* client port to deliver reply to */ }; +/* Entry in thread's list of ports to notify on termination */ +struct lpc_terminate_port_entry +{ + struct list entry; /* entry in thread's lpc_terminate_ports list */ + struct lpc_port *port; /* the LPC port (client port) */ +}; + static const WCHAR lpc_port_name[] = {'L','P','C',' ','P','o','r','t'}; struct type_descr lpc_port_type = @@ -835,3 +848,86 @@ DECL_HANDLER(reply_wait_receive_lpc) reset_port_queue( receive_port ); release_object( port ); } + +/* Register a port for thread termination notification */ +DECL_HANDLER(register_lpc_terminate_port) +{ + struct lpc_port *port; + struct lpc_terminate_port_entry *entry; + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + 0, &lpc_port_ops ); + if (!port) return; + + /* Only client ports should be registered for termination */ + if (port->port_type != PORT_TYPE_CLIENT) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + /* Check if already registered */ + LIST_FOR_EACH_ENTRY( entry, ¤t->lpc_terminate_ports, struct lpc_terminate_port_entry, entry ) + { + if (entry->port == port) + { + release_object( port ); + return; + } + } + + /* Add to thread's terminate port list */ + entry = mem_alloc( sizeof(*entry) ); + if (entry) + { + entry->port = port; /* transfer the reference */ + list_add_tail( ¤t->lpc_terminate_ports, &entry->entry ); + } + else + { + release_object( port ); + } +} + +/* Send LPC_CLIENT_DIED messages to all registered ports for a thread. + * Called from cleanup_thread in thread.c */ +void lpc_send_client_died( struct thread *thread ) +{ + struct lpc_terminate_port_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE( entry, next, &thread->lpc_terminate_ports, + struct lpc_terminate_port_entry, entry ) + { + struct lpc_port *client_port = entry->port; + + /* Send LPC_CLIENT_DIED to the server port via the communication channel */ + if (client_port && client_port->connected_port) + { + struct lpc_port *comm_port = client_port->connected_port; + struct lpc_port *server_port = comm_port->connection_port; + + if (server_port && server_port->port_type == PORT_TYPE_SERVER) + { + struct lpc_message *died_msg = alloc_lpc_message( 0 ); + if (died_msg) + { + died_msg->msg_id = get_next_msg_id(); + died_msg->msg_type = LPC_CLIENT_DIED; + died_msg->client_pid = thread->process->id; + died_msg->client_tid = thread->id; + died_msg->port_context = comm_port->port_context; + + /* Queue on server port */ + list_add_tail( &server_port->msg_queue, &died_msg->entry ); + signal_port_queue( server_port ); + } + } + } + + /* Clean up the entry */ + list_remove( &entry->entry ); + release_object( client_port ); + free( entry ); + } +} diff --git a/server/protocol.def b/server/protocol.def index 6db622cf03c..59ef67714cf 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4367,3 +4367,9 @@ enum inproc_sync_type data_size_t data_size; /* size of message data */ VARARG(data,bytes); /* message data */ @END + + +/* Register port for thread termination notification */ +@REQ(register_lpc_terminate_port) + obj_handle_t handle; /* handle to client port */ +@END diff --git a/server/thread.c b/server/thread.c index 7207f918400..61ae075eb3c 100644 --- a/server/thread.c +++ b/server/thread.c @@ -437,6 +437,7 @@ static inline void init_thread_structure( struct thread *thread ) list_init( &thread->system_apc ); list_init( &thread->user_apc ); list_init( &thread->kernel_object ); + list_init( &thread->lpc_terminate_ports ); for (i = 0; i < MAX_INFLIGHT_FDS; i++) thread->inflight[i].server = thread->inflight[i].client = -1; @@ -609,6 +610,7 @@ static void cleanup_thread( struct thread *thread ) { int i; + lpc_send_client_died( thread ); cleanup_thread_completion( thread ); if (thread->context) { diff --git a/server/thread.h b/server/thread.h index 77ea355483d..e55ad0259f4 100644 --- a/server/thread.h +++ b/server/thread.h @@ -99,6 +99,7 @@ struct thread data_size_t desc_len; /* thread description length in bytes */ WCHAR *desc; /* thread description string */ struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */ + struct list lpc_terminate_ports; /* list of ports to notify on thread termination */ }; extern struct thread *current; @@ -144,6 +145,10 @@ extern void get_thread_context( struct thread *thread, struct context_data *cont extern void set_thread_context( struct thread *thread, const struct context_data *context, unsigned int flags ); extern int send_thread_signal( struct thread *thread, int sig ); +/* LPC functions */ + +extern void lpc_send_client_died( struct thread *thread ); + extern unsigned int global_error; /* global error code for when no thread is current */ static inline unsigned int get_error(void) { return current ? current->error : global_error; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611