8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 22:43:04 +01:00

Fixed CORE-2778: SegFault in services when using same handle in different threads

This commit is contained in:
alexpeshkoff 2009-12-01 11:45:32 +00:00
parent 735a8383d1
commit 5a5489ab40
3 changed files with 116 additions and 26 deletions

View File

@ -223,8 +223,10 @@ namespace
inline void validateHandle(Service* service) inline void validateHandle(Service* service)
{ {
if (!service->checkHandle()) if (service && service->checkHandle())
status_exception::raise(Arg::Gds(isc_bad_svc_handle)); return;
status_exception::raise(Arg::Gds(isc_bad_svc_handle));
} }
class AttachmentHolder class AttachmentHolder

View File

@ -271,11 +271,11 @@ namespace {
}; };
// Generic mutex to synchronize services // Generic mutex to synchronize services
GlobalPtr<Mutex> svc_mutex; GlobalPtr<Mutex> globalServicesMutex;
// All that we need to shutdown service threads when shutdown in progress // All that we need to shutdown service threads when shutdown in progress
typedef Array<Jrd::Service*> AllServices; typedef Array<Jrd::Service*> AllServices;
GlobalPtr<AllServices> allServices; // protected by svc_mutex GlobalPtr<AllServices> allServices; // protected by globalServicesMutex
volatile bool svcShutdown = false; volatile bool svcShutdown = false;
void put_status_arg(ISC_STATUS*& status, const MsgFormat::safe_cell& value) void put_status_arg(ISC_STATUS*& status, const MsgFormat::safe_cell& value)
@ -302,6 +302,46 @@ namespace {
using namespace Jrd; using namespace Jrd;
Service::ExistenceGuard::ExistenceGuard(Service* s)
: svc(s), locked(false)
{
MutexLockGuard guard(globalServicesMutex);
if (! svc->locateInAllServices())
{
// Service is so old that it's even missing in allSevrices array
Arg::Gds(isc_bad_svc_handle).raise();
}
if (svc->svc_flags & SVC_detached)
{
// Service was already detached
Arg::Gds(isc_bad_svc_handle).raise();
}
// Appears we have correct handle, lock it to make sure service exists
// for our lifetime
svc->svc_existence_lock.enter();
fb_assert(!svc->svc_current_guard);
svc->svc_current_guard = this;
locked = true;
}
Service::ExistenceGuard::~ExistenceGuard()
{
release();
}
void Service::ExistenceGuard::release()
{
if (locked)
{
locked = false;
svc->svc_current_guard = NULL;
svc->svc_existence_lock.leave();
}
}
void Service::parseSwitches() void Service::parseSwitches()
{ {
svc_parsed_sw = svc_switches; svc_parsed_sw = svc_switches;
@ -388,7 +428,7 @@ void Service::started()
{ {
if (!(svc_flags & SVC_evnt_fired)) if (!(svc_flags & SVC_evnt_fired))
{ {
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
svc_flags |= SVC_evnt_fired; svc_flags |= SVC_evnt_fired;
svcStart.release(); svcStart.release();
} }
@ -731,7 +771,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d
svc_trusted_login(getPool()), svc_trusted_role(false), svc_uses_security_database(false), svc_trusted_login(getPool()), svc_trusted_role(false), svc_uses_security_database(false),
svc_switches(getPool()), svc_perm_sw(getPool()), svc_address_path(getPool()), svc_switches(getPool()), svc_perm_sw(getPool()), svc_address_path(getPool()),
svc_network_protocol(getPool()), svc_remote_address(getPool()), svc_remote_process(getPool()), svc_network_protocol(getPool()), svc_remote_address(getPool()), svc_remote_process(getPool()),
svc_remote_pid(0) svc_remote_pid(0), svc_current_guard(NULL)
{ {
svc_trace_manager = NULL; svc_trace_manager = NULL;
memset(svc_status, 0, sizeof svc_status); memset(svc_status, 0, sizeof svc_status);
@ -739,7 +779,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d
{ // scope { // scope
// Account service block in global array // Account service block in global array
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
checkForShutdown(); checkForShutdown();
allServices->add(this); allServices->add(this);
} }
@ -950,6 +990,8 @@ static THREAD_ENTRY_DECLARE svcShutdownThread(THREAD_ENTRY_PARAM)
void Service::detach() void Service::detach()
{ {
ExistenceGuard guard(this);
// save it cause after call to finish() we can't access class members any more // save it cause after call to finish() we can't access class members any more
const bool localDoShutdown = svc_do_shutdown; const bool localDoShutdown = svc_do_shutdown;
@ -978,30 +1020,53 @@ Service::~Service()
delete svc_trace_manager; delete svc_trace_manager;
svc_trace_manager = NULL; svc_trace_manager = NULL;
if (svc_current_guard)
{
svc_current_guard->release();
}
} }
void Service::removeFromAllServices() void Service::removeFromAllServices()
{ {
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
AllServices& all(allServices);
for (unsigned int pos = 0; pos < all.getCount(); ++pos) size_t pos;
if (locateInAllServices(&pos))
{ {
if (all[pos] == this) allServices->remove(pos);
{ return;
all.remove(pos);
return;
}
} }
fb_assert(false); fb_assert(false);
} }
bool Service::locateInAllServices(size_t* posPtr)
{
MutexLockGuard guard(globalServicesMutex);
AllServices& all(allServices);
for (size_t pos = 0; pos < all.getCount(); ++pos)
{
if (all[pos] == this)
{
if (posPtr)
{
*posPtr = pos;
}
return true;
}
}
return false;
}
ULONG Service::totalCount() ULONG Service::totalCount()
{ {
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
return allServices->getCount(); return allServices->getCount();
} }
@ -1010,7 +1075,7 @@ bool Service::checkForShutdown()
{ {
if (svcShutdown) if (svcShutdown)
{ {
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
if (svc_flags & SVC_shutdown) if (svc_flags & SVC_shutdown)
{ {
@ -1030,16 +1095,16 @@ void Service::shutdownServices()
{ {
svcShutdown = true; svcShutdown = true;
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
AllServices& all(allServices); AllServices& all(allServices);
for (unsigned int pos = 0; pos < all.getCount(); ) for (unsigned int pos = 0; pos < all.getCount(); )
{ {
if (all[pos]->svc_flags & SVC_thd_running) if (all[pos]->svc_flags & SVC_thd_running)
{ {
svc_mutex->leave(); globalServicesMutex->leave();
THD_sleep(1); THD_sleep(1);
svc_mutex->enter(); globalServicesMutex->enter();
pos = 0; pos = 0;
continue; continue;
} }
@ -1057,6 +1122,8 @@ ISC_STATUS Service::query2(thread_db* tdbb,
USHORT buffer_length, USHORT buffer_length,
UCHAR* info) UCHAR* info)
{ {
ExistenceGuard guard(this);
UCHAR item; UCHAR item;
UCHAR buffer[MAXPATHLEN]; UCHAR buffer[MAXPATHLEN];
USHORT l, length, version, get_flags; USHORT l, length, version, get_flags;
@ -1545,6 +1612,8 @@ void Service::query(USHORT send_item_length,
USHORT buffer_length, USHORT buffer_length,
UCHAR* info) UCHAR* info)
{ {
ExistenceGuard guard(this);
UCHAR item, *p; UCHAR item, *p;
UCHAR buffer[256]; UCHAR buffer[256];
USHORT l, length, version, get_flags; USHORT l, length, version, get_flags;
@ -1927,6 +1996,8 @@ void Service::query(USHORT send_item_length,
void Service::start(USHORT spb_length, const UCHAR* spb_data) void Service::start(USHORT spb_length, const UCHAR* spb_data)
{ {
ExistenceGuard guard(this);
ThreadIdHolder holdId(svc_thread_strings); ThreadIdHolder holdId(svc_thread_strings);
try try
@ -1952,8 +2023,8 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
status_exception::raise(Arg::Gds(isc_bad_spb_form)); status_exception::raise(Arg::Gds(isc_bad_spb_form));
} }
{ // scope for locked svc_mutex { // scope for locked globalServicesMutex
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
if (svc_flags & SVC_thd_running) { if (svc_flags & SVC_thd_running) {
status_exception::raise(Arg::Gds(isc_svc_in_use) << Arg::Str(serv->serv_name)); status_exception::raise(Arg::Gds(isc_svc_in_use) << Arg::Str(serv->serv_name));
@ -2059,7 +2130,7 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
if (serv->serv_thd) if (serv->serv_thd)
{ {
{ // scope { // scope
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
svc_flags &= ~SVC_evnt_fired; svc_flags &= ~SVC_evnt_fired;
svc_flags |= SVC_thd_running; svc_flags |= SVC_thd_running;
} }
@ -2258,7 +2329,7 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
*return_length = 0; *return_length = 0;
{ // scope { // scope
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
svc_flags &= ~SVC_timeout; svc_flags &= ~SVC_timeout;
} }
@ -2282,7 +2353,7 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
#endif #endif
if (timeout && elapsed_time >= timeout) if (timeout && elapsed_time >= timeout)
{ {
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
svc_flags |= SVC_timeout; svc_flags |= SVC_timeout;
break; break;
} }
@ -2323,7 +2394,7 @@ void Service::finish(USHORT flag)
{ {
if (flag == SVC_finished || flag == SVC_detached) if (flag == SVC_finished || flag == SVC_detached)
{ {
MutexLockGuard guard(svc_mutex); MutexLockGuard guard(globalServicesMutex);
svc_flags |= flag; svc_flags |= flag;
if (! (svc_flags & SVC_thd_running)) if (! (svc_flags & SVC_thd_running))

View File

@ -182,6 +182,8 @@ private:
// Service must have private destructor, called from finish // Service must have private destructor, called from finish
// when both (server and client) threads are finished // when both (server and client) threads are finished
~Service(); ~Service();
// Find current service in global services list
bool locateInAllServices(size_t* posPtr = NULL);
// Detach self from global services list // Detach self from global services list
void removeFromAllServices(); void removeFromAllServices();
// The only service, implemented internally // The only service, implemented internally
@ -279,6 +281,21 @@ public:
}; };
private: private:
StatusStringsHelper svc_thread_strings; StatusStringsHelper svc_thread_strings;
//Service existence guard
class ExistenceGuard
{
public:
ExistenceGuard(Service* svc);
~ExistenceGuard();
void release();
private:
Service* svc;
bool locked;
};
friend class ExistenceGuard;
Firebird::Mutex svc_existence_lock;
ExistenceGuard* svc_current_guard;
}; };
} //namespace Jrd } //namespace Jrd