diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index 12a3e0c2eb..2fc20bf79d 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -512,6 +512,7 @@ bool Console::init(bool) // FIXME: looks awfully empty, doesn't it? bool Console::shutdown(void) { + assert(inited); std::lock_guard lock{*wlock}; FreeConsole(); inited = false; diff --git a/library/Core.cpp b/library/Core.cpp index a5cd996667..7cc00f6401 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -127,6 +127,8 @@ namespace DFHack { return Filesystem::getInstallDir() / "hack" / "data" / "dfhack-config-defaults"; }; + Core* Core::active_instance = nullptr; + class MainThread { public: //! MainThread::suspend keeps the main DF thread suspended from Core::Init to @@ -1062,6 +1064,11 @@ df::viewscreen * Core::getTopViewscreen() { } bool Core::InitMainThread() { + assert(active_instance == nullptr); + + // set this instance as the active instance + active_instance = this; + // this hook is always called from DF's main (render) thread, so capture this thread id df_render_thread = std::this_thread::get_id(); @@ -1462,8 +1469,8 @@ bool Core::InitSimulationThread() } Core& Core::getInstance() { - static Core instance; - return instance; + assert(Core::active_instance != nullptr); + return *Core::active_instance; } bool Core::isSuspended(void) @@ -1893,6 +1900,7 @@ int Core::Shutdown ( void ) if (hotkey_mgr) { delete hotkey_mgr; + hotkey_mgr = nullptr; } if(plug_mgr) @@ -1900,13 +1908,21 @@ int Core::Shutdown ( void ) delete plug_mgr; plug_mgr = nullptr; } + // invalidate all modules allModules.clear(); Textures::cleanup(); DFSDL::cleanup(); + + // FIXME console has already been shut down at this point, so getConsole is returning a dead object DFSteam::cleanup(getConsole()); + memset(&(s_mods), 0, sizeof(s_mods)); d.reset(); + + // clear active instance + Core::active_instance = nullptr; + return -1; } diff --git a/library/Debug.cpp b/library/Debug.cpp index dafbeb5ce3..50bef6e538 100644 --- a/library/Debug.cpp +++ b/library/Debug.cpp @@ -66,9 +66,6 @@ void DebugManager::unregisterCategory(DebugCategory& cat) DebugRegisterBase::DebugRegisterBase(DebugCategory* cat) { - // Make sure Core lives at least as long any DebugCategory to - // allow debug prints until all Debugcategories has been destructed - Core::getInstance(); DebugManager::getInstance().registerCategory(*cat); } diff --git a/library/Hooks.cpp b/library/Hooks.cpp index 0f957fea06..4c2c4e76c8 100644 --- a/library/Hooks.cpp +++ b/library/Hooks.cpp @@ -7,6 +7,8 @@ static bool disabled = false; DFhackCExport const int32_t dfhooks_priority = 100; +static std::unique_ptr core_instance; + // called from the main thread before the simulation thread is started // and the main event loop is initiated DFhackCExport void dfhooks_init() { @@ -16,8 +18,11 @@ DFhackCExport void dfhooks_init() { return; } + // construct DFHack core instance + core_instance = std::make_unique(); + // we need to init DF globals before we can check the commandline - if (!DFHack::Core::getInstance().InitMainThread() || !df::global::game) { + if (!core_instance->InitMainThread() || !df::global::game) { // we don't set disabled to true here so symbol generation can work return; } @@ -26,6 +31,8 @@ DFhackCExport void dfhooks_init() { if (cmdline.find("--disable-dfhack") != std::string::npos) { fprintf(stderr, "dfhack: --disable-dfhack specified on commandline; disabling\n"); disabled = true; + core_instance->Shutdown(); + core_instance.reset(); return; } @@ -36,14 +43,16 @@ DFhackCExport void dfhooks_init() { DFhackCExport void dfhooks_shutdown() { if (disabled) return; - DFHack::Core::getInstance().Shutdown(); + core_instance->Shutdown(); + // release DFHack core instance + core_instance.reset(); } // called from the simulation thread in the main event loop DFhackCExport void dfhooks_update() { if (disabled) return; - DFHack::Core::getInstance().Update(); + core_instance->Update(); } // called from the simulation thread just before adding the macro @@ -59,7 +68,7 @@ DFhackCExport void dfhooks_prerender() { DFhackCExport bool dfhooks_sdl_event(SDL_Event* event) { if (disabled) return false; - return DFHack::Core::getInstance().DFH_SDL_Event(event); + return core_instance->DFH_SDL_Event(event); } // called from the main thread just after setting mouse state in gps and just @@ -68,7 +77,7 @@ DFhackCExport void dfhooks_sdl_loop() { if (disabled) return; // TODO: wire this up to the new SDL-based console once it is merged - DFHack::Core::getInstance().DFH_SDL_Loop(); + core_instance->DFH_SDL_Loop(); } // called from the main thread for each utf-8 char read from the ncurses input @@ -78,5 +87,5 @@ DFhackCExport void dfhooks_sdl_loop() { DFhackCExport bool dfhooks_ncurses_key(int key) { if (disabled) return false; - return DFHack::Core::getInstance().DFH_ncurses_key(key); + return core_instance->DFH_ncurses_key(key); } diff --git a/library/include/Core.h b/library/include/Core.h index 1f3cea5837..e76245fbd0 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -157,8 +157,11 @@ namespace DFHack friend void ::dfhooks_sdl_loop(); friend bool ::dfhooks_ncurses_key(int key); public: - /// Get the single Core instance or make one. + /// Get the current active Core instance. will assert if none exists + /// Use noInstance() to check first if unsure static Core& getInstance(); + static bool noInstance() { return active_instance == nullptr; } + /// check if the activity lock is owned by this thread bool isSuspended(void); /// Is everything OK? @@ -259,11 +262,14 @@ namespace DFHack return false; } + Core(); + ~Core(); + private: + static Core* active_instance; + DFHack::Console con; - Core(); - ~Core(); struct Private; std::unique_ptr d; diff --git a/library/include/Debug.h b/library/include/Debug.h index 48e661acc4..632d4910b6 100644 --- a/library/include/Debug.h +++ b/library/include/Debug.h @@ -183,7 +183,7 @@ class DFHACK_EXPORT DebugCategory final { }; /*! - * Fetch a steam object proxy object for output. It also adds standard + * Fetch a stream object proxy object for output. It also adds standard * message components like time and plugin and category names to the line. * * User must make sure that the line is terminated with a line end. @@ -194,6 +194,13 @@ class DFHACK_EXPORT DebugCategory final { */ ostream_proxy_prefix getStream(const level msgLevel) const { + // if the core instance is unavailable, use stderr as a fallback + if (Core::noInstance()) + { + static color_ostream_wrapper fallback{std::cerr}; + return {*this,fallback,msgLevel}; + } + return {*this,Core::getInstance().getConsole(),msgLevel}; } /*!