Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto 2 : // Copyright (c) 2009-2021 The Bitcoin Core developers 3 : // Distributed under the MIT software license, see the accompanying 4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 : 6 : #include <shutdown.h> 7 : 8 : #if defined(HAVE_CONFIG_H) 9 : #include <config/bitcoin-config.h> 10 : #endif 11 : 12 : #include <logging.h> 13 : #include <util/tokenpipe.h> 14 : 15 : #include <node/interface_ui.h> 16 : #include <warnings.h> 17 : 18 : #include <assert.h> 19 : #include <atomic> 20 : #ifdef WIN32 21 : #include <condition_variable> 22 : #endif 23 : 24 0 : bool AbortNode(const std::string& strMessage, bilingual_str user_message) 25 : { 26 0 : SetMiscWarning(Untranslated(strMessage)); 27 0 : LogPrintf("*** %s\n", strMessage); 28 0 : if (user_message.empty()) { 29 0 : user_message = _("A fatal internal error occurred, see debug.log for details"); 30 0 : } 31 0 : AbortError(user_message); 32 0 : StartShutdown(); 33 0 : return false; 34 0 : } 35 : 36 : static std::atomic<bool> fRequestShutdown(false); 37 : static std::atomic<bool> fRequestRestart(false); 38 : 39 : #ifdef WIN32 40 : /** On windows it is possible to simply use a condition variable. */ 41 : std::mutex g_shutdown_mutex; 42 : std::condition_variable g_shutdown_cv; 43 : #else 44 : /** On UNIX-like operating systems use the self-pipe trick. 45 : */ 46 146 : static TokenPipeEnd g_shutdown_r; 47 146 : static TokenPipeEnd g_shutdown_w; 48 : #endif 49 : 50 0 : bool InitShutdownState() 51 : { 52 : #ifndef WIN32 53 0 : std::optional<TokenPipe> pipe = TokenPipe::Make(); 54 0 : if (!pipe) return false; 55 0 : g_shutdown_r = pipe->TakeReadEnd(); 56 0 : g_shutdown_w = pipe->TakeWriteEnd(); 57 : #endif 58 0 : return true; 59 0 : } 60 : 61 0 : void StartShutdown() 62 : { 63 : #ifdef WIN32 64 : std::unique_lock<std::mutex> lk(g_shutdown_mutex); 65 : fRequestShutdown = true; 66 : g_shutdown_cv.notify_one(); 67 : #else 68 : // This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe. 69 : // Make sure that the token is only written once even if multiple threads call this concurrently or in 70 : // case of a reentrant signal. 71 0 : if (!fRequestShutdown.exchange(true)) { 72 : // Write an arbitrary byte to the write end of the shutdown pipe. 73 0 : int res = g_shutdown_w.TokenWrite('x'); 74 0 : if (res != 0) { 75 0 : LogPrintf("Sending shutdown token failed\n"); 76 0 : assert(0); 77 : } 78 0 : } 79 : #endif 80 0 : } 81 0 : void StartRestart() 82 : { 83 0 : fRequestRestart = true; 84 0 : StartShutdown(); 85 0 : } 86 : 87 0 : void AbortShutdown() 88 : { 89 0 : if (fRequestShutdown) { 90 : // Cancel existing shutdown by waiting for it, this will reset condition flags and remove 91 : // the shutdown token from the pipe. 92 0 : WaitForShutdown(); 93 0 : } 94 0 : fRequestShutdown = false; 95 0 : } 96 : 97 25790 : bool ShutdownRequested() 98 : { 99 25790 : return fRequestShutdown; 100 : } 101 : 102 0 : bool RestartRequested() 103 : { 104 0 : return fRequestRestart; 105 : } 106 : 107 0 : void WaitForShutdown() 108 : { 109 : #ifdef WIN32 110 : std::unique_lock<std::mutex> lk(g_shutdown_mutex); 111 : g_shutdown_cv.wait(lk, [] { return fRequestShutdown.load(); }); 112 : #else 113 0 : int res = g_shutdown_r.TokenRead(); 114 0 : if (res != 'x') { 115 0 : LogPrintf("Reading shutdown token failed\n"); 116 0 : assert(0); 117 : } 118 : #endif 119 0 : }