fix a GLib critical from cancelling the pick probe's timeout
The lifetime fix in ffec797 removes the UDPSCOPE_PROBE_PICK_MS timeout after
the nested loop returns, so a row picked before the delay elapsed cannot leave
an armed source pointing at a gone stack frame. But the probe returns
G_SOURCE_REMOVE on every path, so on the ordinary run -- the probe fires, picks
row 0 and the loop quits -- the id was already stale and the removal printed
"Source ID N was not found when attempting to remove it" on every probe run.
The id moves onto the Chooser and pick_probe clears it on entry, so the
cancellation applies only to a probe that never fired.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
ffec797462
commit
53815ad8bc
1 file changed
+10
-3
+10
-3
@@ -60,6 +60,10 @@ struct Chooser {
|
|||||||
GtkWidget* form_group = nullptr;
|
GtkWidget* form_group = nullptr;
|
||||||
GtkWidget* form_iface = nullptr;
|
GtkWidget* form_iface = nullptr;
|
||||||
GtkWidget* form_connect = nullptr;
|
GtkWidget* form_connect = nullptr;
|
||||||
|
/// `UDPSCOPE_PROBE_PICK_MS`'s timeout, kept so a pick made before it fires can cancel it. The
|
||||||
|
/// probe clears it when it runs: `g_source_remove()` on an id whose source already returned
|
||||||
|
/// `G_SOURCE_REMOVE` is a GLib critical, not a no-op.
|
||||||
|
guint pick_source = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void chooser_finish(Chooser& c, bool chosen) {
|
void chooser_finish(Chooser& c, bool chosen) {
|
||||||
@@ -294,6 +298,10 @@ gboolean on_chooser_key(GtkEventControllerKey*, guint keyval, guint, GdkModifier
|
|||||||
/// `UDPSCOPE_PROBE_FIELDS_MS` is in `ui.cpp`.
|
/// `UDPSCOPE_PROBE_FIELDS_MS` is in `ui.cpp`.
|
||||||
gboolean pick_probe(gpointer user) {
|
gboolean pick_probe(gpointer user) {
|
||||||
Chooser& c = *static_cast<Chooser*>(user);
|
Chooser& c = *static_cast<Chooser*>(user);
|
||||||
|
// Every path below returns `G_SOURCE_REMOVE`, so the source is gone the moment this returns and
|
||||||
|
// its id is stale. Clear it here rather than on each path: `choose_source()`'s cancel-the-pending
|
||||||
|
// -probe call would otherwise be a GLib critical on every run that let the probe fire.
|
||||||
|
c.pick_source = 0;
|
||||||
|
|
||||||
// `UDPSCOPE_PROBE_NEWHOST=host:port` drives the form instead of a row.
|
// `UDPSCOPE_PROBE_NEWHOST=host:port` drives the form instead of a row.
|
||||||
if (const char* spec = g_getenv("UDPSCOPE_PROBE_NEWHOST")) {
|
if (const char* spec = g_getenv("UDPSCOPE_PROBE_NEWHOST")) {
|
||||||
@@ -397,16 +405,15 @@ bool choose_source(Config& cfg) {
|
|||||||
// Keep the timeout id so it can be cancelled if the chooser finishes before it fires. A row
|
// Keep the timeout id so it can be cancelled if the chooser finishes before it fires. A row
|
||||||
// clicked before UDPSCOPE_PROBE_PICK_MS elapses would otherwise leave an armed source on the
|
// clicked before UDPSCOPE_PROBE_PICK_MS elapses would otherwise leave an armed source on the
|
||||||
// default main context pointing at the now-gone stack frame.
|
// default main context pointing at the now-gone stack frame.
|
||||||
guint pick_probe_id = 0;
|
|
||||||
if (const char* env = g_getenv("UDPSCOPE_PROBE_PICK_MS")) {
|
if (const char* env = g_getenv("UDPSCOPE_PROBE_PICK_MS")) {
|
||||||
pick_probe_id = g_timeout_add(static_cast<guint>(std::atoi(env)), pick_probe, &c);
|
c.pick_source = g_timeout_add(static_cast<guint>(std::atoi(env)), pick_probe, &c);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.loop = g_main_loop_new(nullptr, FALSE);
|
c.loop = g_main_loop_new(nullptr, FALSE);
|
||||||
g_main_loop_run(c.loop);
|
g_main_loop_run(c.loop);
|
||||||
g_main_loop_unref(c.loop);
|
g_main_loop_unref(c.loop);
|
||||||
c.loop = nullptr;
|
c.loop = nullptr;
|
||||||
if (pick_probe_id != 0) g_source_remove(pick_probe_id);
|
if (c.pick_source != 0) g_source_remove(c.pick_source);
|
||||||
return c.chosen;
|
return c.chosen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user