fix(rpc): trigger UILeave earlier on channel close (#38846)

Problem:
On exit, rpc_free() is called when processing main_loop.events after
libuv calls close callbacks of the channel's stream. However, when there
are no child processes, these libuv callbacks are called in loop_close()
instead of proc_teardown(), and main_loop.events isn't processed after
loop_close(). As a result, calling remote_ui_disconnect() in rpc_free()
causes UILeave to depend on the presence of child processes.

Solution:
Always call remote_ui_disconnect() in rpc_close_event(), and remove the
call in rpc_free().

(cherry picked from commit 5d66ef188f)
This commit is contained in:
zeertzjq
2026-04-08 05:29:08 +08:00
committed by github-actions[bot]
parent a358b9be64
commit eee2d10fd2
3 changed files with 71 additions and 28 deletions

View File

@@ -2684,11 +2684,6 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
{
static bool recursive = false;
#ifdef EXITFREE
if (entered_free_all_mem) {
return;
}
#endif
if (starting == NO_SCREEN) {
return; // user config hasn't been sourced yet
}

View File

@@ -494,32 +494,31 @@ static void rpc_close_event(void **argv)
channel_decref(channel);
// No more I/O can happen on this channel. Remove UI if there is one attached.
// Do this here instead of in rpc_free() which isn't always called on exit, so that
// UILeave events behave consistently.
remote_ui_disconnect(channel->id, NULL, false);
bool is_ui_client = ui_client_channel_id && channel->id == ui_client_channel_id;
if (is_ui_client || channel->streamtype == kChannelStreamStdio) {
if (!is_ui_client) {
// Avoid hanging when there are no other UIs and a prompt is triggered on exit.
remote_ui_disconnect(channel->id, NULL, false);
} else {
ui_client_attach_to_restarted_server();
if (ui_client_channel_id != channel->id) {
// Attached to new server. Don't exit.
return;
}
if (is_ui_client) {
ui_client_attach_to_restarted_server();
if (ui_client_channel_id != channel->id) {
// Attached to new server. Don't exit.
return;
}
if (!channel->detach) {
if (channel->streamtype == kChannelStreamProc && ui_client_error_exit < 0) {
// Wait for the embedded server to exit instead of exiting immediately,
// as it's necessary to get the server's exit code in on_proc_exit().
} else {
exit_on_closed_chan(0);
}
if (channel->streamtype == kChannelStreamProc && ui_client_error_exit < 0) {
// Wait for the embedded server to exit instead of exiting immediately,
// as it's necessary to get the server's exit code in on_proc_exit().
return;
}
exit_on_closed_chan(0);
} else if (channel->streamtype == kChannelStreamStdio && !channel->detach) {
exit_on_closed_chan(0);
}
}
void rpc_free(Channel *channel)
{
remote_ui_disconnect(channel->id, NULL, false);
unpacker_teardown(channel->rpc.unpacker);
xfree(channel->rpc.unpacker);