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

Frontported fix for CORE-3589: Internal shared resource leak. Make trunk work with SystemV semaphores as shared mutexes/events.

This commit is contained in:
alexpeshkoff 2011-10-13 11:31:15 +00:00
parent 8ee9033501
commit 9dab626467
5 changed files with 269 additions and 143 deletions

View File

@ -63,6 +63,7 @@
#include "../common/config/config.h"
#include "../common/utils_proto.h"
#include "../common/StatusArg.h"
#include "../common/ThreadData.h"
#ifdef UNIX
#include <setjmp.h>
@ -94,8 +95,7 @@ static int process_id;
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "../common/classes/semaphore.h"
#include <sys/time.h>
#endif
#ifdef HAVE_FCNTL_H
@ -136,6 +136,7 @@ static size_t getpagesize()
}
#endif
//#define DEBUG_IPC
#ifdef DEBUG_IPC
#define IPC_TRACE(x) { /*time_t t; time(&t); printf("%s", ctime(&t) ); printf x; fflush (stdout);*/ gds__log x; }
#else
@ -306,6 +307,9 @@ namespace {
#ifdef USE_SYS5SEMAPHORE
// Uncomment to trace details of event_init/fini calls
//#define DEB_EVNT
static SLONG create_semaphores(Arg::StatusVector&, SLONG, int);
namespace {
@ -447,6 +451,7 @@ namespace {
if (n >= N_SETS)
{
fb_assert(false); // Not supposed to overflow
return false;
}
@ -526,6 +531,37 @@ namespace {
return n >= 0 ? &sharedFiles[n] : 0;
}
#ifdef DEB_EVNT
struct AbsPtr
{
SLONG offset;
int fn;
bool bad()
{
return offset < 0 || fn < 0;
}
AbsPtr()
: offset(-1), fn(-1)
{ }
bool operator==(const AbsPtr& sec) const
{
return offset == sec.offset && fn == sec.fn;
}
};
static AbsPtr absPtr(const void* s)
{
const int n = getByAddress((UCHAR*) s);
AbsPtr rc;
if (n >= 0)
{
rc.offset = (IPTR)s - (IPTR)(sharedFiles[n].from);
rc.fn = sharedFiles[n].fileNum;
}
return rc;
}
#endif // DEB_EVNT
static void push(const SharedFile& sf)
{
MutexLockGuard guard(mutex);
@ -628,6 +664,7 @@ namespace {
{
semctl(id, 0, IPC_RMID);
}
set[n].fileNum = -1;
}
idCache[n] = -1;
}
@ -641,16 +678,26 @@ namespace {
// Lock init file.
FileLock initLock(status, fd_init, FileLock::OPENED);
if (!initLock.exclusive())
{
iscLogStatus("initLock.exclusive() failed", status.value());
return false;
}
// Find out what file does it belong to.
SharedFile* sf = SharedFile::locate(sem);
if (!sf)
{
gds__log("SharedFile::locate(sem) failed");
return false;
}
return semTable->get(sf->getNum(), sem);
if (!semTable->get(sf->getNum(), sem))
{
gds__log("semTable->get() failed");
return false;
}
return true;
}
void freeSem5(Sys5Semaphore* sem)
@ -667,6 +714,92 @@ namespace {
semTable->put(sem);
}
#ifdef DEB_EVNT
struct Dump
{
SharedFile::AbsPtr e;
enum
{
WORKING, INIT, FINI, INIT_ERR
} state;
int code;
};
GlobalPtr<Array<Dump> > dump;
GlobalPtr<Mutex> dMutex;
Dump* dFind(const event_t* ev)
{
SharedFile::AbsPtr e = SharedFile::absPtr(ev);
fb_assert(!e.bad());
for (Dump* itr = dump->begin(); itr < dump->end(); ++itr)
{
if (itr->e == e)
{
return itr;
}
}
return NULL;
}
void initStart(const event_t* e)
{
MutexLockGuard g(dMutex);
Dump* d = dFind(e);
if (d)
{
fb_assert(d->state == Dump::INIT_ERR);
d->state = Dump::INIT;
}
else
{
Dump dd;
dd.e = SharedFile::absPtr(e);
fb_assert(!dd.e.bad());
dd.state = Dump::INIT;
dd.code = 0;
dump->add(dd);
}
}
void initStop(const event_t* e, int code)
{
MutexLockGuard g(dMutex);
Dump* d = dFind(e);
fb_assert(d && (d->state == Dump::INIT));
d->state = code ? Dump::INIT_ERR : Dump::WORKING;
d->code = code;
}
void finiStart(const event_t* e)
{
MutexLockGuard g(dMutex);
Dump* d = dFind(e);
fb_assert(d && (d->state == Dump::WORKING));
d->state = Dump::FINI;
}
void finiStop(const event_t* e)
{
MutexLockGuard g(dMutex);
Dump* d = dFind(e);
fb_assert(d && (d->state == Dump::FINI));
dump->remove(d);
}
#else // DEB_EVNT
void initStart(const event_t* event) {}
void initStop(const event_t* event, int code) {}
void finiStart(const event_t* event) {}
void finiStop(const event_t* event) {}
#endif // DEB_EVNT
}
int Sys5Semaphore::getId()
@ -741,43 +874,89 @@ static bool event_blocked(const event_t* event, const SLONG value)
namespace {
GlobalPtr<Mutex> timerAccess;
GlobalPtr<Semaphore> timerWakeup, timerFini;
void stopTimers(void*);
bool stopThread = false;
struct TimerEntry
class TimerEntry : public Firebird::RefCntIface<Firebird::ITimer, FB_TIMER_VERSION>
{
SINT64 fireTime;
public:
TimerEntry(int id, USHORT num)
: semId(id), semNum(num)
{ }
void FB_CARG handler()
{
for(;;)
{
union semun arg;
arg.val = 0;
int ret = semctl(semId, semNum, SETVAL, arg);
if (ret != -1)
break;
if (!SYSCALL_INTERRUPTED(errno))
{
gds__log("semctl() failed, errno %d\n", errno);
break;
}
}
}
int FB_CARG release()
{
if (--refCounter == 0)
{
delete this;
return 0;
}
return 1;
}
bool operator== (Sys5Semaphore& sem)
{
return semId == sem.getId() && semNum == sem.semNum;
}
private:
int semId;
USHORT semNum;
static const SINT64& generate(const void* /*sender*/, const TimerEntry& item) { return item.fireTime; }
static THREAD_ENTRY_DECLARE timeThread(THREAD_ENTRY_PARAM);
static void init()
{
int rc = gds__thread_start(timeThread, 0, 0, 0, 0);
if (rc != 0)
{
fatal_exception::raiseFmt("Error starting timer thread");
}
gds__register_cleanup(stopTimers, 0);
}
static void cleanup()
{
stopThread = true;
timerWakeup->release();
timerFini->enter();
}
};
typedef SortedArray<TimerEntry, InlineStorage<TimerEntry, 64>, SINT64, TimerEntry> TimerQueue;
typedef HalfStaticArray<TimerEntry*, 64> TimerQueue;
GlobalPtr<TimerQueue> timerQueue;
GlobalPtr<Mutex> timerAccess;
InitMutex<TimerEntry> timerHolder;
void addTimer(Sys5Semaphore* sem, int microSeconds)
{
TimerEntry* newTimer = new TimerEntry(sem->getId(), sem->semNum);
{
MutexLockGuard guard(timerAccess);
timerQueue->push(newTimer);
}
TimerInterfacePtr()->start(newTimer, microSeconds);
}
void delTimer(Sys5Semaphore* sem)
{
bool found = false;
TimerEntry** t;
{
MutexLockGuard guard(timerAccess);
for (t = timerQueue->begin(); t < timerQueue->end(); ++t)
{
if (**t == *sem)
{
timerQueue->remove(t);
found = true;
break;
}
}
}
if (found)
{
TimerInterfacePtr()->stop(*t);
}
}
SINT64 curTime()
{
@ -789,97 +968,8 @@ SINT64 curTime()
system_call_failed::raise("gettimeofday");
}
SINT64 timeout = ((SINT64) cur_time.tv_sec) * 1000000 + cur_time.tv_usec;
return timeout;
}
SINT64 addTimer(Sys5Semaphore* sem, int microSeconds)
{
timerHolder.init();
TimerEntry newTimer;
newTimer.fireTime = curTime() + microSeconds;
newTimer.semId = sem->getId();
newTimer.semNum = sem->semNum;
MutexLockGuard guard(timerAccess);
timerQueue->add(newTimer);
timerWakeup->release();
return newTimer.fireTime;
}
void delTimer(Sys5Semaphore* sem)
{
const int id = sem->getId();
MutexLockGuard guard(timerAccess);
for (unsigned int i = 0; i < timerQueue->getCount(); ++i)
{
const TimerEntry& e(timerQueue->operator[](i));
if (e.semNum == sem->semNum && e.semId == id)
{
timerQueue->remove(i);
return;
}
}
}
void stopTimers(void*)
{
timerHolder.cleanup();
}
THREAD_ENTRY_DECLARE TimerEntry::timeThread(THREAD_ENTRY_PARAM)
{
while (!stopThread)
{
int microSeconds = 0;
{
MutexLockGuard guard(timerAccess);
const SINT64 cur = curTime();
while (timerQueue->getCount() > 0)
{
const TimerEntry& e(timerQueue->operator[](0));
if (e.fireTime <= cur)
{
for (;;)
{
union semun arg;
arg.val = 0;
int ret = semctl(e.semId, e.semNum, SETVAL, arg);
if (ret != -1)
break;
if (!SYSCALL_INTERRUPTED(errno))
{
break;
}
}
timerQueue->remove((size_t) 0);
}
else
{
microSeconds = e.fireTime - cur;
break;
}
}
}
if (microSeconds)
{
timerWakeup->tryEnter(0, microSeconds / 1000);
}
else
{
timerWakeup->enter();
}
}
timerFini->release();
return 0;
SINT64 rc = ((SINT64) cur_time.tv_sec) * 1000000 + cur_time.tv_usec;
return rc;
}
} // namespace
@ -928,7 +1018,9 @@ void ISC_event_fini(event_t* event)
*
**************************************/
IPC_TRACE(("ISC_event_fini set=%d num=%d\n", event->semSet, event->semNum));
finiStart(event);
freeSem5(event);
finiStop(event);
}
@ -944,12 +1036,14 @@ int ISC_event_init(event_t* event)
* Prepare an event object for use.
*
**************************************/
initStart(event);
event->event_count = 0;
if (!getSem5(event))
{
IPC_TRACE(("ISC_event_init failed get sem %p\n", event));
initStop(event, 1);
return FB_FAILURE;
}
@ -959,11 +1053,13 @@ int ISC_event_init(event_t* event)
arg.val = 0;
if (semctl(event->getId(), event->semNum, SETVAL, arg) < 0)
{
initStop(event, 2);
iscLogStatus("event_init()",
(Arg::Gds(isc_sys_request) << Arg::Str("semctl") << SYS_ERR(errno)).value());
return FB_FAILURE;
}
initStop(event, 0);
return FB_SUCCESS;
}
@ -1031,7 +1127,8 @@ int ISC_event_wait(event_t* event, SLONG value, const SLONG micro_seconds)
SINT64 timeout = 0;
if (micro_seconds > 0)
{
timeout = addTimer(event, micro_seconds);
timeout = curTime() + micro_seconds;
addTimer(event, micro_seconds);
}
// Go into wait loop
@ -3543,8 +3640,7 @@ static SLONG create_semaphores(Arg::StatusVector& statusVector, SLONG key, int s
{
// We want to limit access to semaphores, created here
// Reasonable access rights to them - exactly like security database has
char secDb[MAXPATHLEN];
Auth::SecurityDatabase::getPath(secDb);
const char* secDb = Config::getDefaultConfig()->getSecurityDatabase();
struct stat st;
if (stat(secDb, &st) == 0)
{

View File

@ -150,7 +150,7 @@ EventManager::~EventManager()
{
// Terminate the event watcher thread
m_startupSemaphore.tryEnter(5);
ISC_event_post(&m_process->prb_event);
(void) ISC_event_post(&m_process->prb_event);
m_cleanupSemaphore.tryEnter(5);
#ifdef HAVE_OBJECT_MAP
@ -393,7 +393,11 @@ SLONG EventManager::queEvents(SLONG session_id,
if (flag)
{
post_process((prb*) SRQ_ABS_PTR(m_processOffset));
if (!post_process((prb*) SRQ_ABS_PTR(m_processOffset)))
{
release_shmem();
(Firebird::Arg::Gds(isc_random) << "post_process() failed").raise();
}
}
release_shmem();
@ -513,7 +517,11 @@ void EventManager::deliverEvents()
prb* const process = (prb*) ((UCHAR*) event_srq - OFFSET (prb*, prb_processes));
if (process->prb_flags & PRB_wakeup)
{
post_process(process);
if (!post_process(process))
{
release_shmem();
(Firebird::Arg::Gds(isc_random) << "post_process() failed").raise();
}
flag = true;
break;
}
@ -682,7 +690,11 @@ void EventManager::create_process()
insert_tail(&sh_mem_header->evh_processes, &process->prb_processes);
SRQ_INIT(process->prb_sessions);
ISC_event_init(&process->prb_event);
if (ISC_event_init(&process->prb_event) != FB_SUCCESS)
{
release_shmem();
(Firebird::Arg::Gds(isc_random) << "ISC_event_init() failed").raise();
}
m_processOffset = SRQ_REL_PTR(process);
@ -1205,7 +1217,7 @@ void EventManager::mutex_bugcheck(const TEXT* string, int mutex_state)
}
void EventManager::post_process(prb* process)
bool EventManager::post_process(prb* process)
{
/**************************************
*
@ -1219,7 +1231,7 @@ void EventManager::post_process(prb* process)
**************************************/
process->prb_flags &= ~PRB_wakeup;
process->prb_flags |= PRB_pending;
ISC_event_post(&process->prb_event);
return ISC_event_post(&process->prb_event) == FB_SUCCESS;
}
@ -1424,7 +1436,7 @@ void EventManager::watcher_thread()
if (m_exiting)
break;
ISC_event_wait(&m_process->prb_event, value, 0);
(void) ISC_event_wait(&m_process->prb_event, value, 0);
}
m_cleanupSemaphore.release();

View File

@ -81,7 +81,7 @@ private:
req_int* historical_interest(ses*, SLONG);
void insert_tail(srq*, srq*);
evnt* make_event(USHORT, const TEXT*, SLONG);
void post_process(prb*);
bool post_process(prb*);
void probe_processes();
void release_shmem();
void remove_que(srq*);

View File

@ -250,6 +250,7 @@ LockManager::~LockManager()
m_startupSemaphore.tryEnter(5);
// Wakeup the AST thread - it might be blocking
(void) // Ignore errors in dtor()
ISC_event_post(&m_process->prc_blocking);
// Wait for the AST thread to finish cleanup or for 5 seconds
@ -1549,7 +1550,7 @@ void LockManager::blocking_action_thread()
}
}
ISC_event_wait(&m_process->prc_blocking, value, 0);
(void) ISC_event_wait(&m_process->prc_blocking, value, 0);
}
}
catch (const Firebird::Exception& x)
@ -1736,7 +1737,11 @@ bool LockManager::create_owner(Arg::StatusVector& statusVector,
remove_que(&owner->own_lhb_owners);
}
init_owner_block(owner, owner_type, owner_id);
if (!init_owner_block(statusVector, owner, owner_type, owner_id))
{
release_mutex();
return false;
}
insert_tail(&sh_mem_header->lhb_owners, &owner->own_lhb_owners);
@ -1802,7 +1807,11 @@ bool LockManager::create_process(Arg::StatusVector& statusVector)
insert_tail(&sh_mem_header->lhb_processes, &process->prc_lhb_processes);
ISC_event_init(&process->prc_blocking);
if (ISC_event_init(&process->prc_blocking) != FB_SUCCESS)
{
statusVector << Arg::Gds(isc_lockmanerr);
return false;
}
m_processOffset = SRQ_REL_PTR(process);
@ -2298,7 +2307,7 @@ SRQ_PTR LockManager::grant_or_que(Attachment* attachment, lrq* request, lbl* loc
}
void LockManager::init_owner_block(own* owner, UCHAR owner_type, LOCK_OWNER_T owner_id)
bool LockManager::init_owner_block(Arg::StatusVector& statusVector, own* owner, UCHAR owner_type, LOCK_OWNER_T owner_id)
{
/**************************************
*
@ -2326,7 +2335,13 @@ void LockManager::init_owner_block(own* owner, UCHAR owner_type, LOCK_OWNER_T ow
owner->own_acquire_time = 0;
owner->own_ast_count = 0;
ISC_event_init(&owner->own_wakeup);
if (ISC_event_init(&owner->own_wakeup) != FB_SUCCESS)
{
statusVector << Arg::Gds(isc_lockmanerr);
return false;
}
return true;
}
@ -2916,7 +2931,7 @@ void LockManager::post_wakeup(own* owner)
{
++sh_mem_header->lhb_wakeups;
owner->own_flags |= OWN_wakeup;
ISC_event_post(&owner->own_wakeup);
(void) ISC_event_post(&owner->own_wakeup);
}
}
@ -3067,7 +3082,10 @@ void LockManager::remap_local_owners()
own* owner = (own*) ((UCHAR*) lock_srq - OFFSET(own*, own_prc_owners));
if (owner->own_flags & OWN_waiting)
{
ISC_event_post(&owner->own_wakeup);
if (ISC_event_post(&owner->own_wakeup) != FB_SUCCESS)
{
bug(NULL, "remap failed: ISC_event_post() failed");
}
}
}

View File

@ -365,7 +365,7 @@ private:
lrq* get_request(SRQ_PTR);
void grant(lrq*, lbl*);
SRQ_PTR grant_or_que(Attachment*, lrq*, lbl*, SSHORT);
void init_owner_block(own*, UCHAR, LOCK_OWNER_T);
bool init_owner_block(Firebird::Arg::StatusVector&, own*, UCHAR, LOCK_OWNER_T);
void insert_data_que(lbl*);
void insert_tail(SRQ, SRQ);
bool internal_convert(Attachment* database, Firebird::Arg::StatusVector&, SRQ_PTR, UCHAR, SSHORT,