From b8a976f4870ba5dfc7a6af1ea09c9277b71d686c Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 17:42:24 +0000 Subject: gui: check the two thread spawns the GUI target still leaves unguarded gui.cpp is not in NODE_OBJS, so bitflash-node never sees it -- but it is in GUI_OBJS and `all: bitflash` is the default target, so this is what an operator who runs the windowed build is running. Two spawns there were still unchecked. Enabling pool mode from the options dialog set gPoolServerRunning = true and then called _beginthread without looking at the result, so a refused spawn left the operator with a pool that reports itself running, no RPC server for miners to reach, and nothing in the log. The flag latches, so the branch is never retried either. The restore dialog called std::thread(RestoreThread).detach() bare, which on a refused spawn is an uncaught std::system_error and takes the window down -- the same failure issue #18 was about, in the same tree, one target over. The busy flag stays where it was, set before the spawn and rolled back in the catch. It cannot move after the spawn: RestoreThread clears it itself on the way out, and a restore that finishes quickly would have its `false` overwritten by ours, leaving the dialog greyed out over a search that is not running. Verified by type-checking gui.cpp under the Makefile's exact flags against a stub GLFW/GL header, since this host has neither. That is a syntax and type check, not a link: gui.cpp reaches GLFW through 13 functions and three GL calls, all declared with their real signatures, but the GUI target has not been built here. --- src/gui.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/gui.cpp b/src/gui.cpp index 8dcb854..9b3802b 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "bip32.h" #include "walletcmd.h" #include "font_roboto.h" @@ -910,8 +911,16 @@ static void DrawOptionsDialog() if (nMineMode == MINE_OPERATOR) { if (!gPoolServerRunning) { - gPoolServerRunning = true; - _beginthread(ThreadRPCServer, 0, NULL); + // Claim the server is up only once it is. The flag used to + // be set first and the result never looked at, so a refused + // spawn left an operator with a pool that reports itself + // running, no RPC server for miners to reach, and nothing + // anywhere saying why. The flag also latches: with it set, + // this branch is never retried. + if (_beginthread(ThreadRPCServer, 0, NULL) == (uintptr_t)-1) + printf("Error: _beginthread(ThreadRPCServer) failed -- pool server not started\n"); + else + gPoolServerRunning = true; } else if (!gPoolRunning) { gPoolRunning = true; } @@ -1124,11 +1133,28 @@ static void DrawRestorePhraseDialog() } g_restoreDerived.store(0); g_restoreRecovered.store(0); + // Set before the spawn, not after: RestoreThread clears this + // flag itself when it finishes, and a short restore can beat + // us back here and have its `false` overwritten, leaving the + // dialog disabled forever. g_restoreRunning.store(true); // On its own thread: a restore walks the whole chain once per // batch and would otherwise freeze the window for as long as it - // takes. - std::thread(RestoreThread, std::string(g_restoreInput)).detach(); + // takes. A refused spawn is one restore that does not happen, + // not a reason to take the window down with an uncaught + // std::system_error -- and the flag has to come back off, or + // the dialog sits greyed out reporting a search nobody is + // running. + try + { + std::thread(RestoreThread, std::string(g_restoreInput)).detach(); + } + catch (const std::system_error& e) + { + g_restoreRunning.store(false); + std::lock_guard lock(g_restoreResultMutex); + g_restoreResult = std::string("Could not start the search: ") + e.what(); + } } } if (fBusy) -- cgit v1.2.3