diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-02 17:42:24 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-02 17:42:24 +0000 |
| commit | b8a976f4870ba5dfc7a6af1ea09c9277b71d686c (patch) | |
| tree | 24bc580af6e1616fd1854ebc86f700c508e62a43 | |
| parent | 0a8c445742ce871c6b9352bf406d2232a6fd3134 (diff) | |
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.
| -rw-r--r-- | src/gui.cpp | 34 |
1 files 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 @@ | |||
| 15 | #include <atomic> | 15 | #include <atomic> |
| 16 | #include <mutex> | 16 | #include <mutex> |
| 17 | #include <thread> | 17 | #include <thread> |
| 18 | #include <system_error> | ||
| 18 | #include "bip32.h" | 19 | #include "bip32.h" |
| 19 | #include "walletcmd.h" | 20 | #include "walletcmd.h" |
| 20 | #include "font_roboto.h" | 21 | #include "font_roboto.h" |
| @@ -910,8 +911,16 @@ static void DrawOptionsDialog() | |||
| 910 | 911 | ||
| 911 | if (nMineMode == MINE_OPERATOR) { | 912 | if (nMineMode == MINE_OPERATOR) { |
| 912 | if (!gPoolServerRunning) { | 913 | if (!gPoolServerRunning) { |
| 913 | gPoolServerRunning = true; | 914 | // Claim the server is up only once it is. The flag used to |
| 914 | _beginthread(ThreadRPCServer, 0, NULL); | 915 | // be set first and the result never looked at, so a refused |
| 916 | // spawn left an operator with a pool that reports itself | ||
| 917 | // running, no RPC server for miners to reach, and nothing | ||
| 918 | // anywhere saying why. The flag also latches: with it set, | ||
| 919 | // this branch is never retried. | ||
| 920 | if (_beginthread(ThreadRPCServer, 0, NULL) == (uintptr_t)-1) | ||
| 921 | printf("Error: _beginthread(ThreadRPCServer) failed -- pool server not started\n"); | ||
| 922 | else | ||
| 923 | gPoolServerRunning = true; | ||
| 915 | } else if (!gPoolRunning) { | 924 | } else if (!gPoolRunning) { |
| 916 | gPoolRunning = true; | 925 | gPoolRunning = true; |
| 917 | } | 926 | } |
| @@ -1124,11 +1133,28 @@ static void DrawRestorePhraseDialog() | |||
| 1124 | } | 1133 | } |
| 1125 | g_restoreDerived.store(0); | 1134 | g_restoreDerived.store(0); |
| 1126 | g_restoreRecovered.store(0); | 1135 | g_restoreRecovered.store(0); |
| 1136 | // Set before the spawn, not after: RestoreThread clears this | ||
| 1137 | // flag itself when it finishes, and a short restore can beat | ||
| 1138 | // us back here and have its `false` overwritten, leaving the | ||
| 1139 | // dialog disabled forever. | ||
| 1127 | g_restoreRunning.store(true); | 1140 | g_restoreRunning.store(true); |
| 1128 | // On its own thread: a restore walks the whole chain once per | 1141 | // On its own thread: a restore walks the whole chain once per |
| 1129 | // batch and would otherwise freeze the window for as long as it | 1142 | // batch and would otherwise freeze the window for as long as it |
| 1130 | // takes. | 1143 | // takes. A refused spawn is one restore that does not happen, |
| 1131 | std::thread(RestoreThread, std::string(g_restoreInput)).detach(); | 1144 | // not a reason to take the window down with an uncaught |
| 1145 | // std::system_error -- and the flag has to come back off, or | ||
| 1146 | // the dialog sits greyed out reporting a search nobody is | ||
| 1147 | // running. | ||
| 1148 | try | ||
| 1149 | { | ||
| 1150 | std::thread(RestoreThread, std::string(g_restoreInput)).detach(); | ||
| 1151 | } | ||
| 1152 | catch (const std::system_error& e) | ||
| 1153 | { | ||
| 1154 | g_restoreRunning.store(false); | ||
| 1155 | std::lock_guard<std::mutex> lock(g_restoreResultMutex); | ||
| 1156 | g_restoreResult = std::string("Could not start the search: ") + e.what(); | ||
| 1157 | } | ||
| 1132 | } | 1158 | } |
| 1133 | } | 1159 | } |
| 1134 | if (fBusy) | 1160 | if (fBusy) |