fix(editor): guard against re-entrant launch_editor
The async editor rewrite (g_child_watch + ui_suspend / ui_resume) lost the editor_task.active check the old pthread implementation had at the top of get_message_from_editor_async. With the guard gone, a second launch_editor call while the first session is in flight would call ui_suspend a second time, fork another editor, and register a second child watch; whichever child exits first runs ui_resume and clears suspension while the other still owns the terminal. Triggering this needs a code path other than the main-loop alt+c / /editor (those can't fire while the main loop is parked in the suspended iteration), so the realistic source is a plugin firing launch_editor from a g_main_context_iteration dispatch. Restore an explicit editor_active static in src/tools/editor.c: check it at the start of launch_editor (bail with a console error), set it just before ui_suspend, and clear it on the fork-error path and at the start of _editor_exit_cb. Keeps the same simple semantics the pre-merge code had, without exposing a new ui.h API.
This commit is contained in:
@@ -32,9 +32,20 @@ typedef struct EditorContext
|
|||||||
void* user_data;
|
void* user_data;
|
||||||
} EditorContext;
|
} EditorContext;
|
||||||
|
|
||||||
|
// Set TRUE for the lifetime of an editor session (from launch_editor up to
|
||||||
|
// _editor_exit_cb / fork-error cleanup). The async editor model lost the old
|
||||||
|
// editor_task.active check from the pthread implementation; without this
|
||||||
|
// guard a second launch_editor (e.g. via a plugin dispatched through
|
||||||
|
// g_main_context_iteration) would call ui_suspend again and fork a second
|
||||||
|
// editor that fights the first one for the terminal, then prematurely
|
||||||
|
// ui_resume when either exits.
|
||||||
|
static gboolean editor_active = FALSE;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_editor_exit_cb(GPid pid, gint status, gpointer data)
|
_editor_exit_cb(GPid pid, gint status, gpointer data)
|
||||||
{
|
{
|
||||||
|
editor_active = FALSE;
|
||||||
|
|
||||||
EditorContext* ctx = data;
|
EditorContext* ctx = data;
|
||||||
gchar* contents = NULL;
|
gchar* contents = NULL;
|
||||||
GError* error = NULL;
|
GError* error = NULL;
|
||||||
@@ -70,6 +81,11 @@ _editor_exit_cb(GPid pid, gint status, gpointer data)
|
|||||||
gboolean
|
gboolean
|
||||||
launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* data), void* user_data)
|
launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* data), void* user_data)
|
||||||
{
|
{
|
||||||
|
if (editor_active) {
|
||||||
|
cons_show_error("An editor session is already active.");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
auto_gchar gchar* filename = NULL;
|
auto_gchar gchar* filename = NULL;
|
||||||
auto_gerror GError* glib_error = NULL;
|
auto_gerror GError* glib_error = NULL;
|
||||||
const char* jid = connection_get_barejid();
|
const char* jid = connection_get_barejid();
|
||||||
@@ -118,6 +134,7 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
|||||||
ctx->callback = callback;
|
ctx->callback = callback;
|
||||||
ctx->user_data = user_data;
|
ctx->user_data = user_data;
|
||||||
|
|
||||||
|
editor_active = TRUE;
|
||||||
ui_suspend();
|
ui_suspend();
|
||||||
|
|
||||||
// Force GLib to install its SIGCHLD handler before forking. Creating a
|
// Force GLib to install its SIGCHLD handler before forking. Creating a
|
||||||
@@ -129,6 +146,7 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
log_error("[Editor] Failed to fork: %s", strerror(errno));
|
log_error("[Editor] Failed to fork: %s", strerror(errno));
|
||||||
|
editor_active = FALSE;
|
||||||
ui_resume();
|
ui_resume();
|
||||||
ui_resize();
|
ui_resize();
|
||||||
cons_show_error("Failed to start editor: %s", strerror(errno));
|
cons_show_error("Failed to start editor: %s", strerror(errno));
|
||||||
|
|||||||
Reference in New Issue
Block a user