Fix shutdown race conditions causing bad_weak_ptr crash (v5.4.1)
Build All Platforms / test-linux-unit (push) Waiting to run
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions

Fixes multiple concurrency bugs exposed during shutdown when Tor proxy
connections are failing:

- Reorder shutdown: stop network threads before destroying Tor V3 services
- Make RPC listener responsive to fShutdown (poll_one+sleep vs blocking run_one)
- Wrap StopRequests() in try/catch and drain io_service on exit
- Fix leaked CNode AddRef in ThreadSocketHandler2 and ThreadMessageHandler2
  (return→break so Release loop executes)
- Guard vNodes.size() read with cs_vNodes lock (data race)
- Guard Qt UI signal callbacks with fShutdown check (use-after-free)
- Add cs_vNodes lock in CNetCleanup global destructor
- Force-disconnect remaining nodes in StopNode() after threads stop
- Make Tor maintenance thread sleep in 500ms intervals for prompt shutdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 21:57:34 -07:00
parent b6013edbe4
commit dfb4b221dd
6 changed files with 66 additions and 18 deletions
+17 -2
View File
@@ -946,9 +946,24 @@ void ThreadRPCServer2(void* parg)
vnThreadsRunning[THREAD_RPCLISTENER]--;
while (!fShutdown)
io_service.run_one();
{
// Use poll_one + sleep instead of blocking run_one so the thread
// remains responsive to fShutdown and can exit promptly.
if (!io_service.poll_one())
{
io_service.restart();
MilliSleep(50);
}
}
vnThreadsRunning[THREAD_RPCLISTENER]++;
StopRequests();
// Safely shut down: close acceptors, then drain any remaining handlers
try {
StopRequests();
} catch (...) {
// Absorb bad_weak_ptr or other exceptions from stale tracked slots
}
io_service.poll(); // process cancellation callbacks so shared_ptrs are released
}
class JSONRequest