diff --git a/src/sv2-tp.cpp b/src/sv2-tp.cpp index 419998f1..afac5879 100644 --- a/src/sv2-tp.cpp +++ b/src/sv2-tp.cpp @@ -273,13 +273,13 @@ MAIN_FUNCTION registerSignalHandler(SIGINT, HandleSIGTERM); #endif - while(!g_interrupt) { + while (!g_interrupt && !tp->BackendDisconnected()) { UninterruptibleSleep(100ms); } - tp->Interrupt(); - tp->StopThreads(); + const bool backend_disconnected = tp->BackendDisconnected(); + tp.reset(); - return EXIT_SUCCESS; + return backend_disconnected ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/sv2/template_provider.cpp b/src/sv2/template_provider.cpp index a445bd46..7db69d0c 100644 --- a/src/sv2/template_provider.cpp +++ b/src/sv2/template_provider.cpp @@ -120,7 +120,11 @@ Sv2TemplateProvider::~Sv2TemplateProvider() m_connman->Interrupt(); m_connman->StopThreads(); - Interrupt(); + if (!m_backend_disconnected.load()) { + Interrupt(); + } else { + m_flag_interrupt_sv2 = true; + } StopThreads(); } @@ -132,16 +136,34 @@ void Sv2TemplateProvider::Interrupt() { LOCK(m_tp_mutex); for (auto& t : GetBlockTemplates()) { - t.second.second->interruptWait(); + try { + t.second.second->interruptWait(); + } catch (const ipc::Exception& e) { + HandleBackendDisconnect("interruptWait", e); + } } } m_flag_interrupt_sv2 = true; - m_mining.interrupt(); + try { + m_mining.interrupt(); + } catch (const ipc::Exception& e) { + HandleBackendDisconnect("interrupt", e); + } // Also interrupt network threads so client handlers can wind down quickly. if (m_connman) m_connman->Interrupt(); } +void Sv2TemplateProvider::HandleBackendDisconnect(const char* operation, const std::exception& e) +{ + const bool first_disconnect = !m_backend_disconnected.exchange(true); + if (first_disconnect) { + LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Bitcoin Core IPC connection lost during %s: %s\n", operation, e.what()); + } + m_flag_interrupt_sv2 = true; + if (m_connman) m_connman->Interrupt(); +} + void Sv2TemplateProvider::StopThreads() { if (m_thread_sv2_handler.joinable()) { @@ -188,7 +210,12 @@ void Sv2TemplateProvider::ThreadSv2Handler() // TODO: Wait until there's no headers-only branch with more work than our chaintip. // The current check can still cause us to broadcast a few dozen useless templates // at startup. - if (!m_mining.isInitialBlockDownload()) break; + try { + if (!m_mining.isInitialBlockDownload()) break; + } catch (ipc::Exception& e) { + HandleBackendDisconnect("isInitialBlockDownload", e); + break; + } if (log_ibd == 0) { LogPrintf("Waiting for IBD to complete on %s network before serving templates (this may take a while)\n", ChainTypeToString(gArgs.GetChainType())); @@ -286,7 +313,11 @@ void Sv2TemplateProvider::ThreadSv2ClientHandler(size_t client_id) if (!prepare_block_create_options(block_create_options)) break; const auto time_start{SteadyClock::now()}; - block_template = m_mining.createNewBlock(block_create_options); + try { + block_template = m_mining.createNewBlock(block_create_options); + } catch (ipc::Exception& e) { + HandleBackendDisconnect("createNewBlock", e); + } if (!block_template) { LogPrintLevel(BCLog::SV2, BCLog::Level::Trace, "No new template for client id=%zu, node is shutting down\n", client_id); @@ -352,8 +383,12 @@ void Sv2TemplateProvider::ThreadSv2ClientHandler(size_t client_id) static_cast(fee_delta), client_id); } - - std::shared_ptr tmpl = block_template->waitNext(options); + std::shared_ptr tmpl; + try { + tmpl = block_template->waitNext(options); + } catch (ipc::Exception& e) { + HandleBackendDisconnect("waitNext", e); + } // The client may have disconnected during the wait, check now to avoid // a spurious IPC call and confusing log statements. { @@ -405,7 +440,7 @@ void Sv2TemplateProvider::ThreadSv2ClientHandler(size_t client_id) std::this_thread::sleep_for(50ms); } } - } catch (const std::exception& e) { + } catch (std::exception& e) { LogPrintLevel(BCLog::SV2, BCLog::Level::Trace, "Client thread for id=%zu exiting after exception: %s\n", client_id, e.what()); diff --git a/src/sv2/template_provider.h b/src/sv2/template_provider.h index 5b53bcb6..5b9bfd1f 100644 --- a/src/sv2/template_provider.h +++ b/src/sv2/template_provider.h @@ -11,6 +11,7 @@ #include #include #include +#include using interfaces::BlockTemplate; @@ -111,6 +112,11 @@ class Sv2TemplateProvider : public Sv2EventsInterface using BlockTemplateCache = std::map>>; BlockTemplateCache m_block_template_cache GUARDED_BY(m_tp_mutex); + /** + * flag to check if the backend has been disconnected or not + */ + std::atomic m_backend_disconnected{false}; + public: explicit Sv2TemplateProvider(interfaces::Mining& mining); @@ -168,6 +174,9 @@ class Sv2TemplateProvider : public Sv2EventsInterface /* Block templates that connected clients may be working on */ BlockTemplateCache& GetBlockTemplates() EXCLUSIVE_LOCKS_REQUIRED(m_tp_mutex) { return m_block_template_cache; } + // Accessor to get the backend disconnected state + bool BackendDisconnected() const {return m_backend_disconnected.load();} + private: /* Forget templates from before the last block, but with a few seconds margin. */ @@ -189,6 +198,8 @@ class Sv2TemplateProvider : public Sv2EventsInterface */ [[nodiscard]] bool SendWork(Sv2Client& client, uint64_t template_id, BlockTemplate& block_template, bool future_template); + void HandleBackendDisconnect(const char* operation, const std::exception& e); + }; #endif // BITCOIN_SV2_TEMPLATE_PROVIDER_H