diff --git a/src/common/classes/ImplementHelper.h b/src/common/classes/ImplementHelper.h index 10cb927269..3f25b4d48d 100644 --- a/src/common/classes/ImplementHelper.h +++ b/src/common/classes/ImplementHelper.h @@ -211,7 +211,6 @@ public: { PluginManagerInterfacePtr pi; pi->unregisterModule(this); - pi->moduleUnloaded(); flagOsUnload = false; } diff --git a/src/include/FirebirdPluginApi.h b/src/include/FirebirdPluginApi.h index 881cfc49fe..5eafa98850 100644 --- a/src/include/FirebirdPluginApi.h +++ b/src/include/FirebirdPluginApi.h @@ -172,7 +172,9 @@ public: // Pay attention - this should be called at plugin-regsiter time! // Only at this moment manager knows, which module sets his cleanup virtual void FB_CARG registerModule(IPluginModule* cleanup) = 0; - // Remove registered before cleanup routine + // Remove registered before cleanup routine. + // This method must be called by module which detects that it's unloaded, + // but not notified prior to it by PluginManager via IPluginModule. virtual void FB_CARG unregisterModule(IPluginModule* cleanup) = 0; // Main function called to access plugins registered in plugins manager // Has front-end in GetPlugins.h - template GetPlugins @@ -189,9 +191,6 @@ public: // Plugins must be released using this function - use of plugin's release() // will cause resources leak virtual void FB_CARG releasePlugin(IPluginBase* plugin) = 0; - // This method must be called by module which detects that it's unloaded, - // but not notified prior to it by PluginManager via IPluginModule. - virtual void FB_CARG moduleUnloaded() = 0; }; typedef void PluginEntrypoint(IMaster* masterInterface); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 777c004298..2f5f2e1a48 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -283,18 +283,10 @@ int JService::release() return 0; } -static AtomicCounter shutdownCounter; - int JProvider::release() { if (--refCounter == 0) { - if (--shutdownCounter == 0) - { - LocalStatus status; - shutdown(&status, 5000, fb_shutrsn_no_connection); - } - delete this; return 0; } @@ -302,7 +294,23 @@ int JProvider::release() return 1; } -static Firebird::UnloadDetector unloadDetector; +static UnloadDetector unloadDetector; + + +class ShutdownBeforeUnload +{ +public: + ShutdownBeforeUnload(Firebird::MemoryPool&) + { } + ~ShutdownBeforeUnload() + { + LocalStatus status; + currentProvider()->shutdown(&status, 0, fb_shutrsn_exit_called); + } +}; + +static GlobalPtr shutdownBeforeUnload; + class EngineFactory : public StackIface { @@ -315,7 +323,7 @@ public: return NULL; } - ++shutdownCounter; + //++shutdownCounter; IPluginBase* p = new JProvider(factoryParameter); p->addRef(); return p; @@ -3825,7 +3833,7 @@ private: }; -void JProvider::shutdown(IStatus* status, unsigned int timeout, const int /*reason*/) +void JProvider::shutdown(IStatus* status, unsigned int timeout, const int reason) { /************************************** * @@ -3857,6 +3865,15 @@ void JProvider::shutdown(IStatus* status, unsigned int timeout, const int /*reas attach_count, database_count, svc_count); } + if (reason == fb_shutrsn_exit_called) + { + // Starting threads may fail when task is going to close. + // This happens at least with some microsoft C runtimes. + // If people wish to have timeout, they should better call fb_shutdown() themselves. + // Therefore: + timeout = 0; + } + if (timeout) { Semaphore shutdown_semaphore; diff --git a/src/yvalve/MasterImplementation.cpp b/src/yvalve/MasterImplementation.cpp index 29491bab68..5d1dc1e222 100644 --- a/src/yvalve/MasterImplementation.cpp +++ b/src/yvalve/MasterImplementation.cpp @@ -484,6 +484,8 @@ public: if (stopThread) { // ignore an attempt to start timer - anyway thread to make it fire is down + timer->addRef(); + timer->release(); return; } diff --git a/src/yvalve/PluginManager.cpp b/src/yvalve/PluginManager.cpp index 7a89f9fc3e..c0883ebb95 100644 --- a/src/yvalve/PluginManager.cpp +++ b/src/yvalve/PluginManager.cpp @@ -89,6 +89,7 @@ namespace file += newExt; } + // Holds a reference to plugins.conf file class StaticConfHolder { public: @@ -236,7 +237,9 @@ namespace return NULL; } - struct RegisteredPlugin // This is POD object + // Plugins registered when loading pluign module + // This is POD object - no dtor, only simple data types inside + struct RegisteredPlugin { RegisteredPlugin(IPluginFactory* f, const char* nm, unsigned int t) : factory(f), name(nm), type(t) @@ -251,6 +254,7 @@ namespace unsigned int type; }; + // Controls module, containing plugins class PluginModule : public Firebird::RefCounted, public GlobalStorage { public: @@ -310,6 +314,10 @@ namespace if (cleanup == c) { cleanup = 0; + // This is called only by unregister module + // when current module is forced to go away by OS. + // Do not unload it ourself in this case. + addRef(); } else if (next) { @@ -377,6 +385,8 @@ namespace PluginModule* builtin = NULL; + // Provides most of configuration services for plugins, + // except per-database configuration in aliases.conf class ConfiguredPlugin : public RefCounted, public GlobalStorage { public: @@ -442,6 +452,34 @@ namespace PathName plugName; }; + // Delays destruction of ConfiguredPlugin instance + class PluginDestroyTimer : public Firebird::StdIface + { + public: + PluginDestroyTimer(ConfiguredPlugin* cp) + : configuredPlugin(cp) + { } + + // ITimer implementation + void FB_CARG handler() + { } + + int FB_CARG release() + { + if (--refCounter == 0) + { + delete this; + return 0; + } + + return 1; + } + + private: + RefPtr configuredPlugin; + }; + + // Provides per-database configuration from aliases.conf class FactoryParameter : public StdIface { public: @@ -484,6 +522,12 @@ namespace } private: + ~FactoryParameter() + { + PluginDestroyTimer* timer = new PluginDestroyTimer(configuredPlugin); + TimerInterfacePtr()->start(timer, 1000000); // 1 sec + } + RefPtr configuredPlugin; RefPtr firebirdConf; }; @@ -586,6 +630,7 @@ namespace *prev = this; } + // Provides access to plugins of given type / name class PluginSet : public StdIface { public: @@ -848,9 +893,16 @@ void FB_CARG PluginManager::registerModule(IPluginModule* cleanup) void FB_CARG PluginManager::unregisterModule(IPluginModule* cleanup) { - MutexLockGuard g(plugins->mutex); + { // guard scope + MutexLockGuard g(plugins->mutex); + modules->resetCleanup(cleanup); + } - modules->resetCleanup(cleanup); + // Module cleanup should be unregistered only if it's unoaded + // and only if it's unoaded not by PluginManager, but by OS. + // That means that task is closing unexpectedly - sooner of all + // exit() is called by client of embedded server. Shutdown ourself. + fb_shutdown(5000, fb_shutrsn_exit_called); } IPluginSet* FB_CARG PluginManager::getPlugins(unsigned int interfaceType, const char* namesList, @@ -934,9 +986,4 @@ void PluginManager::waitForType(unsigned int typeThatMustGoAway) } } -void FB_CARG PluginManager::moduleUnloaded() -{ - fb_shutdown(5000, fb_shutrsn_exit_called); -} - } // namespace Firebird diff --git a/src/yvalve/PluginManager.h b/src/yvalve/PluginManager.h index 006ced8a3d..2d30772471 100644 --- a/src/yvalve/PluginManager.h +++ b/src/yvalve/PluginManager.h @@ -51,7 +51,6 @@ public: void FB_CARG releasePlugin(IPluginBase* plugin); void FB_CARG registerModule(IPluginModule* cleanup); void FB_CARG unregisterModule(IPluginModule* cleanup); - void FB_CARG moduleUnloaded(); PluginManager();