mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 06:43:04 +01:00
Additional means to avoid crash\hangup when application unload fbclient.dll not calling fb_shutdown.
Fixed reason of 500 ms delay experienced by such applications (described by Arno in fb-devel). Fixed missing assignment of thread priorities.
This commit is contained in:
parent
07c83f9c53
commit
af286a2520
@ -38,6 +38,7 @@
|
||||
#ifdef WIN_NT
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
#include "../common/dllinst.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
@ -319,7 +320,12 @@ void Thread::start(ThreadEntryPoint* routine, void* arg, int priority_arg, Handl
|
||||
|
||||
void Thread::waitForCompletion(Handle& handle)
|
||||
{
|
||||
WaitForSingleObject(handle, 500);
|
||||
// When current DLL is unloading, OS loader holds loader lock. When thread is
|
||||
// exiting, OS notifies every DLL about it, and acquires loader lock. In such
|
||||
// scenario waiting on thread handle will never succeed.
|
||||
if (!Firebird::dDllUnloadTID) {
|
||||
WaitForSingleObject(handle, 500);
|
||||
}
|
||||
CloseHandle(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ namespace Firebird {
|
||||
|
||||
HINSTANCE hDllInst = 0;
|
||||
bool bDllProcessExiting = false;
|
||||
DWORD dDllUnloadTID = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -37,6 +37,7 @@ namespace Firebird {
|
||||
|
||||
extern HINSTANCE hDllInst;
|
||||
extern bool bDllProcessExiting;
|
||||
extern DWORD dDllUnloadTID;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -226,7 +226,9 @@ ModuleLoader::Module* ModuleLoader::loadModule(const PathName& modPath)
|
||||
|
||||
Win32Module::~Win32Module()
|
||||
{
|
||||
if (module)
|
||||
// If we in process of unloading of some DLL, don't unload modules manually
|
||||
// else we could hang up waiting for OS loader lock.
|
||||
if (module && !dDllUnloadTID)
|
||||
FreeLibrary(module);
|
||||
}
|
||||
|
||||
|
@ -332,7 +332,7 @@ namespace Jrd {
|
||||
|
||||
// ready to go
|
||||
guard.leave(); // release in advance to avoid races with cryptThread()
|
||||
Thread::start(cryptThreadStatic, (THREAD_ENTRY_PARAM) this, 0, &cryptThreadId);
|
||||
Thread::start(cryptThreadStatic, (THREAD_ENTRY_PARAM) this, THREAD_medium, &cryptThreadId);
|
||||
}
|
||||
catch (const Firebird::Exception&)
|
||||
{
|
||||
|
@ -47,6 +47,7 @@ BOOL WINAPI DllMain(HINSTANCE h, DWORD reason, LPVOID reserved)
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
bDllProcessExiting = (reserved != NULL);
|
||||
dDllUnloadTID = GetCurrentThreadId();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -849,7 +849,7 @@ void Service::detach()
|
||||
if (localDoShutdown)
|
||||
{
|
||||
// run in separate thread to avoid blocking in remote
|
||||
Thread::start(svcShutdownThread, 0, 0);
|
||||
Thread::start(svcShutdownThread, 0, THREAD_medium);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,16 @@ ConfigStorage::ConfigStorage()
|
||||
|
||||
ConfigStorage::~ConfigStorage()
|
||||
{
|
||||
fb_assert(!m_timer);
|
||||
}
|
||||
|
||||
void ConfigStorage::shutdown()
|
||||
{
|
||||
if (!m_timer)
|
||||
return;
|
||||
|
||||
m_timer->stop();
|
||||
m_timer = NULL;
|
||||
|
||||
::close(m_cfg_file);
|
||||
m_cfg_file = -1;
|
||||
@ -153,6 +162,7 @@ ConfigStorage::~ConfigStorage()
|
||||
m_sharedMemory->removeMapFile();
|
||||
}
|
||||
}
|
||||
m_sharedMemory = NULL;
|
||||
}
|
||||
|
||||
void ConfigStorage::mutexBug(int state, const TEXT* string)
|
||||
|
@ -65,6 +65,7 @@ public:
|
||||
void acquire();
|
||||
void release();
|
||||
|
||||
void shutdown();
|
||||
private:
|
||||
void mutexBug(int osErrorCode, const char* text);
|
||||
bool initialize(Firebird::SharedMemoryBase*, bool);
|
||||
|
@ -163,6 +163,7 @@ void TraceManager::shutdown()
|
||||
init_factories = false;
|
||||
}
|
||||
}
|
||||
getStorage()->shutdown();
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "../common/isc_proto.h"
|
||||
#include "../common/ThreadStart.h"
|
||||
#include "../common/utils_proto.h"
|
||||
#include "../common/dllinst.h"
|
||||
#include "../jrd/ibase.h"
|
||||
#include "../yvalve/utl_proto.h"
|
||||
|
||||
@ -167,6 +168,7 @@ GlobalPtr<Mutex> timerAccess;
|
||||
GlobalPtr<Mutex> timerPause;
|
||||
|
||||
GlobalPtr<Semaphore> timerWakeup;
|
||||
GlobalPtr<Semaphore> timerCleanup;
|
||||
// Should use atomic flag for thread stop to provide correct membar
|
||||
AtomicCounter stopTimerThread(0);
|
||||
|
||||
@ -180,7 +182,7 @@ struct TimerEntry
|
||||
|
||||
static void init()
|
||||
{
|
||||
Thread::start(timeThread, 0, 0, &timerThreadHandle);
|
||||
Thread::start(timeThread, 0, THREAD_high, &timerThreadHandle);
|
||||
}
|
||||
|
||||
static void cleanup();
|
||||
@ -199,6 +201,7 @@ void TimerEntry::cleanup()
|
||||
stopTimerThread.setValue(1);
|
||||
timerWakeup->release();
|
||||
}
|
||||
timerCleanup->tryEnter(5);
|
||||
Thread::waitForCompletion(timerThreadHandle);
|
||||
|
||||
while (timerQueue->hasData())
|
||||
@ -241,7 +244,25 @@ TimerEntry* getTimer(ITimer* timer)
|
||||
|
||||
THREAD_ENTRY_DECLARE TimerEntry::timeThread(THREAD_ENTRY_PARAM)
|
||||
{
|
||||
while (stopTimerThread.value() == 0)
|
||||
#ifdef WIN_NT
|
||||
// The timer thread could unload plugins. Plugins almost always linked with
|
||||
// dispatcher (fbclient.dll) thus, when plugin unloaded it decrement usage
|
||||
// count of fbclient.dll. If application unload fbclient.dll not calling
|
||||
// fb_shutdown, then last unloaded plugin will finally unload fbclient.dll
|
||||
// and the code that is currently running, leading to AV.
|
||||
// To prevent such scenario we increment usage count of fbclient.dll and
|
||||
// will decrement it in safe way at the end of the timer thread.
|
||||
|
||||
char buff[MAX_PATH];
|
||||
GetModuleFileName(hDllInst, buff, sizeof(buff));
|
||||
HMODULE hDll = LoadLibrary(buff);
|
||||
#endif
|
||||
|
||||
while (stopTimerThread.value() == 0
|
||||
#ifdef WIN_NT
|
||||
&& Firebird::dDllUnloadTID == 0
|
||||
#endif
|
||||
)
|
||||
{
|
||||
ISC_UINT64 microSeconds = 0;
|
||||
|
||||
@ -251,7 +272,7 @@ THREAD_ENTRY_DECLARE TimerEntry::timeThread(THREAD_ENTRY_PARAM)
|
||||
|
||||
const ISC_UINT64 cur = curTime();
|
||||
|
||||
while (timerQueue->getCount() > 0)
|
||||
if (timerQueue->getCount() > 0)
|
||||
{
|
||||
TimerEntry e(timerQueue->operator[](0));
|
||||
|
||||
@ -264,11 +285,11 @@ THREAD_ENTRY_DECLARE TimerEntry::timeThread(THREAD_ENTRY_PARAM)
|
||||
|
||||
e.timer->handler();
|
||||
e.timer->release();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
microSeconds = e.fireTime - cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -283,6 +304,19 @@ THREAD_ENTRY_DECLARE TimerEntry::timeThread(THREAD_ENTRY_PARAM)
|
||||
}
|
||||
}
|
||||
|
||||
timerCleanup->release();
|
||||
|
||||
#ifdef WIN_NT
|
||||
if (Firebird::dDllUnloadTID)
|
||||
// fb_shutdown is called as result of FreeLibrary, not by application.
|
||||
// Sooner of all we are the last user of fbclient.dll, and code will be
|
||||
// physically unloaded as result of FreeLibrary() call.
|
||||
FreeLibraryAndExitThread(hDll, 0);
|
||||
else
|
||||
// It is safe to decrement usage count here
|
||||
FreeLibrary(hDll);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "../common/ScanDir.h"
|
||||
#include "../common/classes/GenericMap.h"
|
||||
#include "../common/db_alias.h"
|
||||
#include "../common/dllinst.h"
|
||||
|
||||
#include "../yvalve/config/os/config_root.h"
|
||||
|
||||
@ -1050,6 +1051,10 @@ void PluginManager::unregisterModule(IPluginModule* cleanup)
|
||||
// and only if it's unloaded 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 ourselves.
|
||||
#ifdef WIN_NT
|
||||
if (!Firebird::dDllUnloadTID)
|
||||
Firebird::dDllUnloadTID = GetCurrentThreadId();
|
||||
#endif
|
||||
fb_shutdown(5000, fb_shutrsn_exit_called);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user