8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 11:23:02 +01:00
firebird-mirror/src/common/isc_sync.cpp

3706 lines
82 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: isc_sync.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: General purpose but non-user routines.
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "XENIX" port
2002-02-16 04:05:21 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "DELTA" port
2002-02-16 04:27:33 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
*
* 2002-02-23 Sean Leyne - Code Cleanup, removed old M88K and NCR3000 port
*
* 2002.10.27 Sean Leyne - Completed removal of obsolete "DG_X86" port
* 2002.10.27 Sean Leyne - Completed removal of obsolete "M88K" port
*
2002-10-29 03:45:09 +01:00
* 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "SGI" port
2002-10-29 03:45:09 +01:00
*
2002-10-30 07:40:58 +01:00
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-04-29 00:43:34 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <stdlib.h>
#include <string.h>
2003-03-04 04:42:23 +01:00
#ifdef SOLARIS
#include "../common/gdsassert.h"
2003-03-04 04:42:23 +01:00
#endif
2007-11-12 16:18:49 +01:00
#ifdef HPUX
2001-05-23 15:26:42 +02:00
#include <sys/pstat.h>
#endif
#include "../common/common.h"
#include "gen/iberror.h"
2010-10-12 10:02:57 +02:00
#include "../yvalve/gds_proto.h"
#include "../common/isc_proto.h"
#include "../common/os/isc_i_proto.h"
#include "../common/os/os_utils.h"
#include "../common/isc_s_proto.h"
#include "../common/file_params.h"
#include "../common/gdsassert.h"
#include "../common/config/config.h"
#include "../common/utils_proto.h"
#include "../common/StatusArg.h"
#include "../common/ThreadData.h"
2001-05-23 15:26:42 +02:00
static int process_id;
2009-11-23 08:36:52 +01:00
// Unix specific stuff
2001-05-23 15:26:42 +02:00
#ifdef UNIX
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#ifdef HAVE_SYS_SIGNAL_H
#include <sys/signal.h>
2002-11-01 14:21:17 +01:00
#endif
2001-05-23 15:26:42 +02:00
#include <errno.h>
#ifdef HAVE_UNISTD_H
2001-05-23 15:26:42 +02:00
#include <unistd.h>
#endif
#ifdef USE_SYS5SEMAPHORE
2001-05-23 15:26:42 +02:00
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/time.h>
#endif
2001-05-23 15:26:42 +02:00
2008-12-11 11:59:26 +01:00
#ifdef HAVE_FCNTL_H
2001-05-23 15:26:42 +02:00
#include <fcntl.h>
#endif
#include <sys/mman.h>
#define FTOK_KEY 15
#define PRIV 0666
2009-06-02 08:27:23 +02:00
//#ifndef SHMEM_DELTA
//#define SHMEM_DELTA (1 << 22)
//#endif
2001-05-23 15:26:42 +02:00
2009-06-02 08:27:23 +02:00
//#ifndef SIGURG
//#define SIGURG SIGINT
//#endif
2001-05-23 15:26:42 +02:00
2002-11-21 11:20:07 +01:00
#ifndef HAVE_SEMUN
union semun
{
2001-05-23 15:26:42 +02:00
int val;
struct semid_ds *buf;
ushort *array;
};
#endif
#endif // UNIX
2001-05-23 15:26:42 +02:00
2002-07-06 07:32:02 +02:00
#ifdef HAVE_SYS_PARAM_H
2002-07-02 11:49:19 +02:00
#include <sys/param.h>
2002-07-06 07:32:02 +02:00
#endif
#ifndef HAVE_GETPAGESIZE
2008-12-20 09:12:19 +01:00
static size_t getpagesize()
{
return PAGESIZE;
}
#endif
2002-07-02 11:49:19 +02:00
//#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
#define IPC_TRACE(x)
#endif
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// Windows NT
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
#include <process.h>
#include <windows.h>
#endif
using namespace Jrd;
using namespace Firebird;
static void error(Arg::StatusVector&, const TEXT*, ISC_STATUS);
static bool event_blocked(const event_t* event, const SLONG value);
#ifdef UNIX
static GlobalPtr<Mutex> openFdInit;
namespace {
2008-10-21 06:25:49 +02:00
// File lock holder
class FileLock
{
public:
enum LockLevel {LCK_NONE, LCK_SHARED, LCK_EXCL};
enum DtorMode {CLOSED, OPENED, LOCKED};
FileLock(Arg::StatusVector& aStatusVector, int pFd, DtorMode pMode = CLOSED)
: statusVector(aStatusVector), level(LCK_NONE), fd(pFd), dtorMode(pMode)
2008-10-11 04:42:01 +02:00
{ }
2008-10-21 06:25:49 +02:00
~FileLock()
{
switch (dtorMode)
{
case LOCKED:
break;
case OPENED:
unlock();
break;
case CLOSED:
unlock();
close(fd);
break;
}
}
// unlocking can only put error into log file - we can't throw in dtors
2008-10-21 06:25:49 +02:00
void unlock()
{
if (level == LCK_NONE)
{
return;
}
2008-10-21 06:25:49 +02:00
#ifdef HAVE_FLOCK
if (flock(fd, LOCK_UN))
#else
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_whence = 0;
lock.l_start = 0;
lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1)
#endif
{
Arg::StatusVector local;
error(local, NAME, errno);
iscLogStatus("Unlock error", local.value());
}
level = LCK_NONE;
}
2008-10-11 04:42:01 +02:00
// Call it to keep file locked & opened after dtor is called
void setDtorMode(DtorMode mode) throw()
{
dtorMode = mode;
}
2008-10-11 04:42:01 +02:00
// Call when using already locked file in ctor
void setLevel(LockLevel l)
{
level = l;
}
2008-10-11 04:42:01 +02:00
// All lock methods return true on success, false on error
bool exclusive()
{
return doLock(false, true);
}
bool tryExclusive()
{
return doLock(false, false);
}
bool shared()
{
return doLock(true, true);
}
bool tryShared()
{
return doLock(true, false);
}
private:
Arg::StatusVector& statusVector;
LockLevel level;
int fd;
DtorMode dtorMode;
static const char* NAME;
private:
bool doLock(bool shared, bool wait)
{
const LockLevel newLevel = shared ? LCK_SHARED : LCK_EXCL;
if (newLevel == level)
{
return true;
}
if (level != LCK_NONE)
{
unlock();
}
#ifdef HAVE_FLOCK
2008-10-21 06:25:49 +02:00
if (flock(fd, (shared ? LOCK_SH : LOCK_EX) | (wait ? 0 : LOCK_NB)))
#else //use FCNTL
struct flock lock;
lock.l_type = shared ? F_RDLCK : F_WRLCK;
lock.l_whence = 0;
lock.l_start = 0;
lock.l_len = 0;
2008-10-21 06:25:49 +02:00
if (fcntl(fd, wait ? F_SETLKW : F_SETLK, &lock) == -1)
#endif
{
error(statusVector, NAME, errno);
return false;
}
level = newLevel;
return true;
}
};
2008-10-21 06:25:49 +02:00
const char* FileLock::NAME =
#ifdef HAVE_FLOCK
"flock";
#else //use FCNTL
"fcntl";
#endif
}
#ifdef USE_SYS5SEMAPHORE
// Uncomment to trace details of event_init/fini calls
//#define DEB_EVNT
static SLONG create_semaphores(Arg::StatusVector&, SLONG, int);
namespace {
int fdSem = -1;
int sharedCount = 0;
int fd_init = -1;
2009-06-06 20:13:57 +02:00
// this class is mapped into shared file
class SemTable
{
public:
const static int N_FILES = 8;
const static int N_SETS = 256;
#if defined(DEV_BUILD) || defined(FREEBSD)
const static int SEM_PER_SET = 4; // force multiple sets allocation || work with default freebsd kernel
#else
2008-10-21 06:25:49 +02:00
const static int SEM_PER_SET = 31; // hard limit for some old systems, might set to 32
#endif
const static unsigned char CURRENT_VERSION = 1;
unsigned char version;
private:
int lastSet;
2008-12-20 09:12:19 +01:00
struct
{
char name[MAXPATHLEN];
} filesTable[N_FILES];
2008-12-20 09:12:19 +01:00
struct
{
key_t semKey;
int fileNum;
SLONG mask;
int get(int fNum)
{
if (fileNum == fNum && mask != 0)
{
for (int bit = 0; bit < SEM_PER_SET; ++bit)
{
if (mask & (1 << bit))
{
mask &= ~(1 << bit);
return bit;
}
}
// bad bits in mask ?
mask = 0;
}
return -1;
}
int create(int fNum)
{
fileNum = fNum;
mask = 1 << SEM_PER_SET;
--mask;
mask &= ~1;
return 0;
}
void put(int bit)
{
// fb_assert(!(mask & (1 << bit)));
mask |= (1 << bit);
}
} set[N_SETS];
2008-10-21 06:25:49 +02:00
public:
void cleanup(int fNum, bool release);
2008-10-16 10:52:33 +02:00
key_t getKey(int semSet) const
{
fb_assert(semSet >= 0 && semSet < lastSet);
return set[semSet].semKey;
}
void init()
{
if (sharedCount)
{
return;
}
2008-10-11 04:42:01 +02:00
ftruncate(fdSem, sizeof(*this));
2008-10-11 04:42:01 +02:00
2008-10-21 06:25:49 +02:00
for (int i = 0; i < N_SETS; ++i)
{
if (set[i].fileNum > 0)
{
// may be some old data about really active semaphore sets?
if (version == CURRENT_VERSION)
{
2009-06-02 08:27:23 +02:00
const int semId = semget(set[i].semKey, SEM_PER_SET, 0);
if (semId > 0)
{
semctl(semId, 0, IPC_RMID);
}
}
set[i].fileNum = 0;
}
}
for (int i = 0; i < N_FILES; ++i)
{
filesTable[i].name[0] = 0;
}
version = CURRENT_VERSION;
lastSet = 0;
}
bool get(int fileNum, Sys5Semaphore* sem)
{
// try to locate existing set
int n;
2008-12-02 08:09:49 +01:00
for (n = 0; n < lastSet; ++n)
{
2008-10-16 10:52:33 +02:00
const int semNum = set[n].get(fileNum);
if (semNum >= 0)
{
sem->semSet = n;
sem->semNum = semNum;
return true;
}
}
// create new set
2008-12-02 08:09:49 +01:00
for (n = 0; n < lastSet; ++n)
{
if (set[n].fileNum <= 0)
{
break;
}
}
2008-10-11 04:42:01 +02:00
if (n >= N_SETS)
{
fb_assert(false); // Not supposed to overflow
return false;
}
2008-10-11 04:42:01 +02:00
if (n >= lastSet)
{
lastSet = n + 1;
}
set[n].semKey = ftok(filesTable[fileNum - 1].name, n);
sem->semSet = n;
sem->semNum = set[n].create(fileNum);
return true;
}
void put(Sys5Semaphore* sem)
{
fb_assert(sem->semSet >= 0 && sem->semSet < N_SETS);
set[sem->semSet].put(sem->semNum);
}
2008-10-16 10:52:33 +02:00
int findFileByName(const PathName& name) const
{
// Get a file ID in filesTable.
for (int fileId = 0; fileId < N_FILES; ++fileId)
{
if (name == filesTable[fileId].name)
{
return fileId + 1;
}
}
// not found
return 0;
}
int addFileByName(const PathName& name)
{
int id = findFileByName(name);
if (id > 0)
{
return id;
}
// Get a file ID in filesTable.
for (int fileId = 0; fileId < SemTable::N_FILES; ++fileId)
{
if (filesTable[fileId].name[0] == 0)
{
name.copyTo(filesTable[fileId].name, sizeof(filesTable[fileId].name));
return fileId + 1;
}
}
// not found
fb_assert(false);
return 0;
}
};
2008-10-11 04:42:01 +02:00
SemTable* semTable = NULL;
class SharedFile
{
public:
SharedFile(const char* pName, void* address, int length)
: fileNum(semTable->addFileByName(pName)), from((UCHAR*) address), to(from + length)
{ }
2009-01-03 10:14:29 +01:00
SharedFile() : fileNum(0), from(0), to(0) { }
int getNum() const { return fileNum; }
static SharedFile* locate(void* s)
{
2008-12-20 09:12:19 +01:00
const int n = getByAddress((UCHAR*) s);
return n >= 0 ? &sharedFiles[n] : 0;
}
#ifdef DEB_EVNT
struct AbsPtr
{
AbsPtr()
: offset(-1), fn(-1)
{ }
2011-10-27 03:04:14 +02:00
bool operator==(const AbsPtr& sec) const
{
return offset == sec.offset && fn == sec.fn;
}
2011-10-27 03:04:14 +02:00
bool bad()
{
return offset < 0 || fn < 0;
}
SLONG offset;
int fn;
};
static AbsPtr absPtr(const void* s)
{
const int n = getByAddress((UCHAR*) s);
AbsPtr rc;
2011-10-27 03:04:14 +02:00
if (n >= 0)
{
2011-10-27 03:04:14 +02:00
rc.offset = (IPTR) s - (IPTR) (sharedFiles[n].from);
rc.fn = sharedFiles[n].fileNum;
}
2011-10-27 03:04:14 +02:00
return rc;
}
#endif // DEB_EVNT
static void push(const SharedFile& sf)
{
MutexLockGuard guard(mutex);
IPC_TRACE(("+add SF with %p %p\n", sf.from, sf.to));
sharedFiles.push(sf);
}
static void pop()
{
MutexLockGuard guard(mutex);
SharedFile sf = sharedFiles.pop();
IPC_TRACE(("-pop SF with %p %p\n", sf.from, sf.to));
}
static void remove(void* s)
{
MutexLockGuard guard(mutex);
2008-10-11 04:42:01 +02:00
int n = getByAddress((UCHAR*) s);
2009-12-13 14:49:43 +01:00
if (n >= 0)
{
IPC_TRACE(("-rem SF with %p %p\n", sharedFiles[n].from, sharedFiles[n].to));
sharedFiles.remove(n);
}
else {
IPC_TRACE(("-rem SF Failedp\n"));
}
}
static void remap(void* const f, void* const to, int newLength)
{
MutexLockGuard guard(mutex);
UCHAR* const from = (UCHAR*) f;
for (unsigned int n = 0; n < sharedFiles.getCount(); ++n)
{
if (from == sharedFiles[n].from)
{
sharedFiles[n].from = (UCHAR*) to;
sharedFiles[n].to = sharedFiles[n].from + newLength;
return;
}
}
}
typedef Vector<SharedFile, SemTable::N_FILES> Storage;
private:
int fileNum;
const UCHAR* from;
const UCHAR* to;
static Storage sharedFiles;
static GlobalPtr<Mutex> mutex;
2008-10-16 10:52:33 +02:00
static int getByAddress(UCHAR* const s)
{
MutexLockGuard guard(mutex);
for (unsigned int n = 0; n < sharedFiles.getCount(); ++n)
{
if (s >= sharedFiles[n].from && s < sharedFiles[n].to)
{
return n;
}
}
return -1;
}
};
2008-10-11 04:42:01 +02:00
SharedFile::Storage SharedFile::sharedFiles;
GlobalPtr<Mutex> SharedFile::mutex;
int idCache[SemTable::N_SETS];
GlobalPtr<Mutex> idCacheMutex;
void initCache()
{
MutexLockGuard guard(idCacheMutex);
memset(idCache, 0xff, sizeof idCache);
}
void SemTable::cleanup(int fNum, bool release)
{
fb_assert(fNum > 0 && fNum <= N_FILES);
if (release)
{
filesTable[fNum - 1].name[0] = 0;
}
MutexLockGuard guard(idCacheMutex);
2008-12-02 08:09:49 +01:00
for (int n = 0; n < lastSet; ++n)
{
if (set[n].fileNum == fNum)
{
if (release)
{
Sys5Semaphore sem;
sem.semSet = n;
int id = sem.getId();
if (id >= 0)
{
semctl(id, 0, IPC_RMID);
}
set[n].fileNum = -1;
}
idCache[n] = -1;
}
}
}
bool getSem5(Sys5Semaphore* sem)
{
Arg::StatusVector status;
// 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;
}
if (!semTable->get(sf->getNum(), sem))
{
gds__log("semTable->get() failed");
return false;
}
return true;
}
2008-10-21 06:25:49 +02:00
void freeSem5(Sys5Semaphore* sem)
{
Arg::StatusVector status;
// Lock init file.
FileLock initLock(status, fd_init, FileLock::OPENED);
if (!initLock.exclusive())
{
2009-12-06 03:28:00 +01:00
iscLogStatus("freeSem5 failed to lock init file", status.value());
return;
}
semTable->put(sem);
}
#ifdef DEB_EVNT
struct Dump
{
SharedFile::AbsPtr e;
enum
{
WORKING, INIT, FINI, INIT_ERR
} state;
int code;
};
2011-10-27 03:04:14 +02:00
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()
{
MutexLockGuard guard(idCacheMutex);
fb_assert(semSet >= 0 && semSet < SemTable::N_SETS);
2008-10-11 04:42:01 +02:00
int id = idCache[semSet];
2008-10-11 04:42:01 +02:00
if (id < 0)
{
Arg::StatusVector status;
id = create_semaphores(status, semTable->getKey(semSet), SemTable::SEM_PER_SET);
if (id >= 0)
{
idCache[semSet] = id;
}
else
{
2009-12-06 03:28:00 +01:00
iscLogStatus("create_semaphores failed:", status.value());
}
}
2008-10-11 04:42:01 +02:00
return id;
}
2008-10-11 04:42:01 +02:00
#endif // USE_SYS5SEMAPHORE
2008-10-11 04:42:01 +02:00
#endif // UNIX
2001-05-23 15:26:42 +02:00
#if defined(WIN_NT)
static bool make_object_name(TEXT*, size_t, const TEXT*, const TEXT*);
2001-05-23 15:26:42 +02:00
#endif
static bool event_blocked(const event_t* event, const SLONG value)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e v e n t _ b l o c k e d
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* If a wait would block, return true.
2001-05-23 15:26:42 +02:00
*
**************************************/
2009-06-23 15:26:12 +02:00
if (event->event_count >= value)
{
2003-02-10 01:03:54 +01:00
#ifdef DEBUG_ISC_SYNC
printf("event_blocked: FALSE (eg something to report)\n");
fflush(stdout);
2001-05-23 15:26:42 +02:00
#endif
return false;
}
2001-05-23 15:26:42 +02:00
2003-02-10 01:03:54 +01:00
#ifdef DEBUG_ISC_SYNC
2008-01-16 10:07:24 +01:00
printf("event_blocked: TRUE (eg nothing happened yet)\n");
2004-04-29 00:43:34 +02:00
fflush(stdout);
2001-05-23 15:26:42 +02:00
#endif
2008-01-16 10:07:24 +01:00
return true;
2001-05-23 15:26:42 +02:00
}
#ifdef USE_POSIX_THREADS
#ifdef USE_SYS5SEMAPHORE
namespace {
class TimerEntry : public Firebird::RefCntIface<Firebird::ITimer, FB_TIMER_VERSION>
{
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;
};
typedef HalfStaticArray<TimerEntry*, 64> TimerQueue;
GlobalPtr<TimerQueue> timerQueue;
GlobalPtr<Mutex> timerAccess;
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()
{
struct timeval cur_time;
struct timezone tzUnused;
if (gettimeofday(&cur_time, &tzUnused) != 0)
{
system_call_failed::raise("gettimeofday");
}
SINT64 rc = ((SINT64) cur_time.tv_sec) * 1000000 + cur_time.tv_usec;
return rc;
}
2009-06-23 15:26:12 +02:00
} // namespace
SLONG ISC_event_clear(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ e v e n t _ c l e a r ( S Y S V )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Clear an event preparatory to waiting on it. The order of
* battle for event synchronization is:
*
* 1. Clear event.
* 2. Test data structure for event already completed
* 3. Wait on event.timerQueue[0].
2001-05-23 15:26:42 +02:00
*
**************************************/
union semun arg;
2001-05-23 15:26:42 +02:00
arg.val = 1;
if (semctl(event->getId(), event->semNum, SETVAL, arg) < 0)
{
2009-12-06 03:28:00 +01:00
iscLogStatus("event_clear()",
(Arg::Gds(isc_sys_request) << Arg::Str("semctl") << SYS_ERR(errno)).value());
}
return (event->event_count + 1);
}
void ISC_event_fini(event_t* event)
{
/**************************************
*
* I S C _ e v e n t _ f i n i ( S Y S V )
*
**************************************
*
* Functional description
* Discard an event object.
*
**************************************/
IPC_TRACE(("ISC_event_fini set=%d num=%d\n", event->semSet, event->semNum));
finiStart(event);
freeSem5(event);
finiStop(event);
}
int ISC_event_init(event_t* event)
{
/**************************************
*
* I S C _ e v e n t _ i n i t ( S Y S V )
*
**************************************
*
* Functional description
* 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;
}
IPC_TRACE(("ISC_event_init set=%d num=%d\n", event->semSet, event->semNum));
union semun arg;
arg.val = 0;
if (semctl(event->getId(), event->semNum, SETVAL, arg) < 0)
{
initStop(event, 2);
2009-12-06 03:28:00 +01:00
iscLogStatus("event_init()",
(Arg::Gds(isc_sys_request) << Arg::Str("semctl") << SYS_ERR(errno)).value());
return FB_FAILURE;
}
initStop(event, 0);
return FB_SUCCESS;
}
int ISC_event_post(event_t* event)
{
/**************************************
*
* I S C _ e v e n t _ p o s t ( S Y S V )
*
**************************************
*
* Functional description
* Post an event to wake somebody else up.
*
**************************************/
union semun arg;
++event->event_count;
2009-06-23 15:26:12 +02:00
for (;;)
{
arg.val = 0;
int ret = semctl(event->getId(), event->semNum, SETVAL, arg);
if (ret != -1)
break;
2009-06-23 15:26:12 +02:00
if (!SYSCALL_INTERRUPTED(errno))
{
gds__log("ISC_event_post: semctl failed with errno = %d", errno);
return FB_FAILURE;
2001-05-23 15:26:42 +02:00
}
}
2001-05-23 15:26:42 +02:00
return FB_SUCCESS;
}
2009-12-13 14:49:43 +01:00
int ISC_event_wait(event_t* event, SLONG value, const SLONG micro_seconds)
{
/**************************************
*
* I S C _ e v e n t _ w a i t ( S Y S V )
*
**************************************
*
* Functional description
* Wait on an event. If timeout limit specified, return
* anyway after the timeout even if no event has
* happened. If returning due to timeout, return
* FB_FAILURE else return FB_SUCCESS.
*
**************************************/
2009-06-04 15:16:34 +02:00
//sigset_t mask, oldmask;
2009-11-23 08:36:52 +01:00
// If we're not blocked, the rest is a gross waste of time
if (!event_blocked(event, value))
return FB_SUCCESS;
2009-11-23 08:36:52 +01:00
// Set up timers if a timeout period was specified.
SINT64 timeout = 0;
2008-10-21 06:25:49 +02:00
if (micro_seconds > 0)
{
timeout = curTime() + micro_seconds;
addTimer(event, micro_seconds);
}
2009-11-23 08:36:52 +01:00
// Go into wait loop
int ret = FB_SUCCESS;
2008-10-21 06:25:49 +02:00
for (;;)
{
if (!event_blocked(event, value))
break;
struct sembuf sb;
sb.sem_op = 0;
sb.sem_flg = 0;
sb.sem_num = event->semNum;
int rc = semop(event->getId(), &sb, 1);
if (rc == -1 && !SYSCALL_INTERRUPTED(errno))
{
gds__log("ISC_event_wait: semop failed with errno = %d", errno);
}
2008-10-21 06:25:49 +02:00
if (micro_seconds > 0)
{
// distinguish between timeout and actually happened event
if (! event_blocked(event, value))
break;
// had timeout expired?
if (curTime() >= timeout) // really expired
{
ret = FB_FAILURE;
break;
}
}
}
2009-11-23 08:36:52 +01:00
// Cancel the handler. We only get here if a timeout was specified.
2008-10-21 06:25:49 +02:00
if (micro_seconds > 0)
{
delTimer(event);
}
return ret;
2001-05-23 15:26:42 +02:00
}
#else //not USE_SYS5SEMAPHORE
namespace {
int isPthreadError(int rc, const char* function)
{
if (rc == 0)
return 0;
2009-12-06 03:28:00 +01:00
iscLogStatus("Pthread Error",
(Arg::Gds(isc_sys_request) << Arg::Str(function) << Arg::Unix(rc)).value());
return rc;
}
}
#define PTHREAD_ERROR(x) if (isPthreadError((x), #x)) return FB_FAILURE
2009-04-11 21:41:01 +02:00
#define PTHREAD_ERRNO(x) { int tmpState = (x); if (isPthreadError(tmpState, #x)) return tmpState; }
#define LOG_PTHREAD_ERROR(x) isPthreadError((x), #x)
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
SLONG ISC_event_clear(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ e v e n t _ c l e a r ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
* Clear an event preparatory to waiting on it. The order of
* battle for event synchronization is:
*
* 1. Clear event.
* 2. Test data structure for event already completed
* 3. Wait on event.
*
**************************************/
LOG_PTHREAD_ERROR(pthread_mutex_lock(event->event_mutex));
const SLONG ret = event->event_count + 1;
LOG_PTHREAD_ERROR(pthread_mutex_unlock(event->event_mutex));
2001-05-23 15:26:42 +02:00
return ret;
}
2004-02-02 12:02:12 +01:00
void ISC_event_fini(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ e v e n t _ f i n i ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
* Discard an event object.
*
**************************************/
if (event->pid == getpid())
{
LOG_PTHREAD_ERROR(pthread_mutex_destroy(event->event_mutex));
LOG_PTHREAD_ERROR(pthread_cond_destroy(event->event_cond));
2001-05-23 15:26:42 +02:00
}
}
int ISC_event_init(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ e v e n t _ i n i t ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
* Prepare an event object for use.
*
**************************************/
event->event_count = 0;
event->pid = getpid();
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// Prepare an Inter-Process event block
pthread_mutexattr_t mattr;
pthread_condattr_t cattr;
2001-05-23 15:26:42 +02:00
PTHREAD_ERROR(pthread_mutexattr_init(&mattr));
PTHREAD_ERROR(pthread_condattr_init(&cattr));
2008-01-17 17:17:31 +01:00
#ifdef PTHREAD_PROCESS_SHARED
PTHREAD_ERROR(pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED));
PTHREAD_ERROR(pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED));
2008-01-16 10:07:24 +01:00
#else
2008-01-17 17:17:31 +01:00
#error Your system must support PTHREAD_PROCESS_SHARED to use firebird.
#endif
PTHREAD_ERROR(pthread_mutex_init(event->event_mutex, &mattr));
PTHREAD_ERROR(pthread_cond_init(event->event_cond, &cattr));
PTHREAD_ERROR(pthread_mutexattr_destroy(&mattr));
PTHREAD_ERROR(pthread_condattr_destroy(&cattr));
2001-05-23 15:26:42 +02:00
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
}
2004-02-02 12:02:12 +01:00
int ISC_event_post(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ e v e n t _ p o s t ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
* Post an event to wake somebody else up.
*
**************************************/
PTHREAD_ERROR(pthread_mutex_lock(event->event_mutex));
2001-05-23 15:26:42 +02:00
++event->event_count;
const int ret = pthread_cond_broadcast(event->event_cond);
PTHREAD_ERROR(pthread_mutex_unlock(event->event_mutex));
2001-05-23 15:26:42 +02:00
if (ret)
{
gds__log ("ISC_event_post: pthread_cond_broadcast failed with errno = %d", ret);
return FB_FAILURE;
}
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
}
2009-12-13 14:49:43 +01:00
int ISC_event_wait(event_t* event, const SLONG value, const SLONG micro_seconds)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ e v e n t _ w a i t ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
* Wait on an event. If timeout limit specified, return
* anyway after the timeout even if no event has
* happened. If returning due to timeout, return
* FB_FAILURE else return FB_SUCCESS.
2001-05-23 15:26:42 +02:00
*
**************************************/
2009-11-23 08:36:52 +01:00
// If we're not blocked, the rest is a gross waste of time
2001-05-23 15:26:42 +02:00
if (!event_blocked(event, value))
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// Set up timers if a timeout period was specified.
2001-05-23 15:26:42 +02:00
struct timespec timer;
2009-06-23 15:26:12 +02:00
if (micro_seconds > 0)
{
2001-05-23 15:26:42 +02:00
timer.tv_sec = time(NULL);
timer.tv_sec += micro_seconds / 1000000;
timer.tv_nsec = 1000 * (micro_seconds % 1000000);
}
int ret = FB_SUCCESS;
pthread_mutex_lock(event->event_mutex);
2009-06-23 15:26:12 +02:00
for (;;)
{
if (!event_blocked(event, value))
{
ret = FB_SUCCESS;
2001-05-23 15:26:42 +02:00
break;
}
2009-11-23 08:36:52 +01:00
// The Posix pthread_cond_wait & pthread_cond_timedwait calls
// atomically release the mutex and start a wait.
// The mutex is reacquired before the call returns.
2001-05-23 15:26:42 +02:00
if (micro_seconds > 0)
{
ret = pthread_cond_timedwait(event->event_cond, event->event_mutex, &timer);
2001-05-23 15:26:42 +02:00
#if (defined LINUX || defined DARWIN || defined HP11 || defined FREEBSD)
if (ret == ETIMEDOUT)
2001-05-23 15:26:42 +02:00
#else
if (ret == ETIME)
2001-05-23 15:26:42 +02:00
#endif
{
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// The timer expired - see if the event occurred and return
// FB_SUCCESS or FB_FAILURE accordingly.
2001-05-23 15:26:42 +02:00
if (event_blocked(event, value))
ret = FB_FAILURE;
else
ret = FB_SUCCESS;
break;
}
2001-05-23 15:26:42 +02:00
}
else
ret = pthread_cond_wait(event->event_cond, event->event_mutex);
2001-05-23 15:26:42 +02:00
}
pthread_mutex_unlock(event->event_mutex);
2001-05-23 15:26:42 +02:00
return ret;
}
2008-10-11 04:42:01 +02:00
#endif // USE_SYS5SEMAPHORE
#endif // USE_POSIX_THREADS
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
2004-02-02 12:02:12 +01:00
SLONG ISC_event_clear(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ e v e n t _ c l e a r ( W I N _ N T )
*
**************************************
*
* Functional description
* Clear an event preparatory to waiting on it. The order of
* battle for event synchronization is:
*
* 1. Clear event.
* 2. Test data structure for event already completed
* 3. Wait on event.
*
**************************************/
ResetEvent((HANDLE) event->event_handle);
2008-01-16 10:07:24 +01:00
return event->event_count + 1;
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:07:24 +01:00
void ISC_event_fini(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-01-16 10:07:24 +01:00
* I S C _ e v e n t _ f i n i ( W I N _ N T )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
2008-01-16 10:07:24 +01:00
* Discard an event object.
2001-05-23 15:26:42 +02:00
*
**************************************/
if (event->event_pid == process_id)
{
CloseHandle((HANDLE) event->event_handle);
}
2001-05-23 15:26:42 +02:00
}
int ISC_event_init(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-01-16 10:07:24 +01:00
* I S C _ e v e n t _ i n i t ( W I N _ N T )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
2008-01-16 10:07:24 +01:00
* Prepare an event object for use.
2001-05-23 15:26:42 +02:00
*
**************************************/
2011-05-09 12:15:19 +02:00
static AtomicCounter idCounter;
event->event_id = ++idCounter;
2008-01-16 10:07:24 +01:00
event->event_pid = process_id = getpid();
event->event_count = 0;
event->event_handle = ISC_make_signal(true, true, process_id, event->event_id);
2008-01-16 10:07:24 +01:00
return (event->event_handle) ? FB_SUCCESS : FB_FAILURE;
2001-05-23 15:26:42 +02:00
}
2004-02-02 12:02:12 +01:00
int ISC_event_post(event_t* event)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-01-16 10:07:24 +01:00
* I S C _ e v e n t _ p o s t ( W I N _ N T )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Post an event to wake somebody else up.
*
**************************************/
2008-01-16 10:07:24 +01:00
++event->event_count;
if (event->event_pid != process_id)
return ISC_kill(event->event_pid, event->event_id, event->event_handle);
2008-02-13 13:57:04 +01:00
return SetEvent((HANDLE) event->event_handle) ? FB_SUCCESS : FB_FAILURE;
2001-05-23 15:26:42 +02:00
}
2009-12-13 14:49:43 +01:00
int ISC_event_wait(event_t* event, const SLONG value, const SLONG micro_seconds)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-01-16 10:07:24 +01:00
* I S C _ e v e n t _ w a i t ( W I N _ N T )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Wait on an event.
*
**************************************/
2009-11-23 08:36:52 +01:00
// If we're not blocked, the rest is a gross waste of time
2001-05-23 15:26:42 +02:00
if (!event_blocked(event, value)) {
return FB_SUCCESS;
2008-01-16 10:07:24 +01:00
}
2009-11-23 08:36:52 +01:00
// Go into wait loop
2008-01-16 10:07:24 +01:00
const DWORD timeout = (micro_seconds > 0) ? micro_seconds / 1000 : INFINITE;
2009-06-23 15:26:12 +02:00
for (;;)
{
if (!event_blocked(event, value)) {
return FB_SUCCESS;
2008-01-16 10:07:24 +01:00
}
2009-06-02 08:27:23 +02:00
const DWORD status = WaitForSingleObject(event->event_handle, timeout);
2008-01-16 10:07:24 +01:00
2009-06-02 08:27:23 +02:00
if (status != WAIT_OBJECT_0)
2008-01-16 10:07:24 +01:00
{
return FB_FAILURE;
2008-01-16 10:07:24 +01:00
}
}
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:07:24 +01:00
#endif // WIN_NT
2001-05-23 15:26:42 +02:00
#ifdef UNIX
ULONG ISC_exception_post(ULONG sig_num, const TEXT* err_msg)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2002-02-16 04:05:21 +01:00
* I S C _ e x c e p t i o n _ p o s t ( U N I X )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
2002-02-16 04:05:21 +01:00
* When we got a sync exception, fomulate the error code
2001-05-23 15:26:42 +02:00
* write it to the log file, and abort.
*
* 08-Mar-2004, Nickolay Samofatov.
* This function is dangerous and requires rewrite using signal-safe operations only.
* Main problem is that we call a lot of signal-unsafe functions from this signal handler,
2008-10-21 06:25:49 +02:00
* examples are gds__alloc, gds__log, etc... sprintf is safe on some BSD platforms,
* but not on Linux. This may result in lock-up during signal handling.
*
2001-05-23 15:26:42 +02:00
**************************************/
// If there's no err_msg, we asumed the switch() finds no case or we crash.
// Too much goodwill put on the caller. Weak programming style.
// Therefore, lifted this safety net from the NT version.
if (!err_msg)
{
err_msg = "";
}
2009-06-07 11:49:58 +02:00
TEXT* const log_msg = (TEXT *) gds__alloc(strlen(err_msg) + 256);
// NOMEM: crash!
log_msg[0] = '\0';
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
switch (sig_num)
{
2001-05-23 15:26:42 +02:00
case SIGSEGV:
sprintf(log_msg, "%s Segmentation Fault.\n"
"\t\tThe code attempted to access memory\n"
"\t\twithout privilege to do so.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case SIGBUS:
sprintf(log_msg, "%s Bus Error.\n"
"\t\tThe code caused a system bus error.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case SIGILL:
sprintf(log_msg, "%s Illegal Instruction.\n"
"\t\tThe code attempted to perfrom an\n"
"\t\tillegal operation."
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case SIGFPE:
sprintf(log_msg, "%s Floating Point Error.\n"
"\t\tThe code caused an arithmetic exception\n"
"\t\tor floating point exception."
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
default:
sprintf(log_msg, "%s Unknown Exception.\n"
2006-01-18 11:43:13 +01:00
"\t\tException number %"ULONGFORMAT"."
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg, sig_num);
break;
}
2009-06-23 15:26:12 +02:00
if (err_msg)
{
2001-05-23 15:26:42 +02:00
gds__log(log_msg);
gds__free(log_msg);
}
abort();
return 0; // compiler silencer
2001-05-23 15:26:42 +02:00
}
#endif // UNIX
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
ULONG ISC_exception_post(ULONG except_code, const TEXT* err_msg)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2002-02-16 04:05:21 +01:00
* I S C _ e x c e p t i o n _ p o s t ( W I N _ N T )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
2002-02-16 04:05:21 +01:00
* When we got a sync exception, fomulate the error code
2001-05-23 15:26:42 +02:00
* write it to the log file, and abort. Note: We can not
* actually call "abort" since in windows this will cause
* a dialog to appear stating the obvious! Since on NT we
* would not get a core file, there is actually no difference
2002-02-16 04:05:21 +01:00
* between abort() and exit(3).
2001-05-23 15:26:42 +02:00
*
**************************************/
ULONG result = 0;
bool is_critical = true;
if (!err_msg)
{
err_msg = "";
}
TEXT* log_msg = (TEXT*) gds__alloc(strlen(err_msg) + 256);
// NOMEM: crash!
log_msg[0] = '\0';
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
switch (except_code)
{
2001-05-23 15:26:42 +02:00
case EXCEPTION_ACCESS_VIOLATION:
sprintf(log_msg, "%s Access violation.\n"
"\t\tThe code attempted to access a virtual\n"
"\t\taddress without privilege to do so.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
sprintf(log_msg, "%s Datatype misalignment.\n"
"\t\tThe attempted to read or write a value\n"
"\t\tthat was not stored on a memory boundary.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
sprintf(log_msg, "%s Array bounds exceeded.\n"
"\t\tThe code attempted to access an array\n"
"\t\telement that is out of bounds.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
sprintf(log_msg, "%s Float denormal operand.\n"
"\t\tOne of the floating-point operands is too\n"
"\t\tsmall to represent as a standard floating-point\n"
"\t\tvalue.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
sprintf(log_msg, "%s Floating-point divide by zero.\n"
"\t\tThe code attempted to divide a floating-point\n"
"\t\tvalue by a floating-point divisor of zero.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_FLT_INEXACT_RESULT:
sprintf(log_msg, "%s Floating-point inexact result.\n"
"\t\tThe result of a floating-point operation cannot\n"
"\t\tbe represented exactly as a decimal fraction.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_FLT_INVALID_OPERATION:
sprintf(log_msg, "%s Floating-point invalid operand.\n"
"\t\tAn indeterminant error occurred during a\n"
"\t\tfloating-point operation.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_FLT_OVERFLOW:
sprintf(log_msg, "%s Floating-point overflow.\n"
"\t\tThe exponent of a floating-point operation\n"
"\t\tis greater than the magnitude allowed.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_FLT_STACK_CHECK:
sprintf(log_msg, "%s Floating-point stack check.\n"
"\t\tThe stack overflowed or underflowed as the\n"
"result of a floating-point operation.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_FLT_UNDERFLOW:
sprintf(log_msg, "%s Floating-point underflow.\n"
"\t\tThe exponent of a floating-point operation\n"
"\t\tis less than the magnitude allowed.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
sprintf(log_msg, "%s Integer divide by zero.\n"
"\t\tThe code attempted to divide an integer value\n"
"\t\tby an integer divisor of zero.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_INT_OVERFLOW:
sprintf(log_msg, "%s Interger overflow.\n"
"\t\tThe result of an integer operation caused the\n"
"\t\tmost significant bit of the result to carry.\n"
2002-07-02 11:49:19 +02:00
"\tThis exception will cause the Firebird server\n"
2001-05-23 15:26:42 +02:00
"\tto terminate abnormally.", err_msg);
break;
case EXCEPTION_STACK_OVERFLOW:
2010-06-26 03:18:53 +02:00
status_exception::raise(Arg::Gds(isc_exception_stack_overflow));
2009-11-23 08:36:52 +01:00
// This will never be called, but to be safe it's here
result = (ULONG) EXCEPTION_CONTINUE_EXECUTION;
is_critical = false;
break;
2001-05-23 15:26:42 +02:00
case EXCEPTION_BREAKPOINT:
case EXCEPTION_SINGLE_STEP:
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
case EXCEPTION_INVALID_DISPOSITION:
case EXCEPTION_PRIV_INSTRUCTION:
case EXCEPTION_IN_PAGE_ERROR:
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_GUARD_PAGE:
2009-11-23 08:36:52 +01:00
// Pass these exception on to someone else, probably the OS or the debugger,
// since there isn't a dam thing we can do with them
result = EXCEPTION_CONTINUE_SEARCH;
is_critical = false;
2001-05-23 15:26:42 +02:00
break;
2009-11-23 08:36:52 +01:00
case 0xE06D7363: // E == Exception. 0x6D7363 == "msc". Intel and Borland use the same code to be compatible
// If we've caught our own software exception,
// continue rewinding the stack to properly handle it
// and deliver an error information to the client side
result = EXCEPTION_CONTINUE_SEARCH;
is_critical = false;
break;
default:
sprintf (log_msg, "%s An exception occurred that does\n"
"\t\tnot have a description. Exception number %"XLONGFORMAT".\n"
"\tThis exception will cause the Firebird server\n"
"\tto terminate abnormally.", err_msg, except_code);
2008-10-21 06:25:49 +02:00
break;
2001-05-23 15:26:42 +02:00
}
2003-03-12 14:20:23 +01:00
if (is_critical)
{
gds__log(log_msg);
}
gds__free(log_msg);
if (is_critical)
{
2009-06-23 15:26:12 +02:00
if (Config::getBugcheckAbort())
{
// Pass exception to outer handler in case debugger is present to collect memory dump
return EXCEPTION_CONTINUE_SEARCH;
}
2008-01-16 10:07:24 +01:00
// Silently exit so guardian or service manager can restart the server.
// If exception is getting out of the application Windows displays a message
// asking if you want to send report to Microsoft or attach debugger,
// application is not terminated until you press some button on resulting window.
2008-10-21 06:25:49 +02:00
// This happens even if you run application as non-interactive service on
2008-01-16 10:07:24 +01:00
// "server" OS like Windows Server 2003.
exit(3);
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:07:24 +01:00
return result;
2001-05-23 15:26:42 +02:00
}
#endif // WIN_NT
2001-05-23 15:26:42 +02:00
void SharedMemoryBase::removeMapFile()
{
#ifndef WIN_NT
TEXT expanded_filename[MAXPATHLEN];
gds__prefix_lock(expanded_filename, sh_mem_name);
2009-04-11 21:41:01 +02:00
// We can't do much (specially in dtors) when it fails
// therefore do not check for errors - at least it's just /tmp.
unlink(expanded_filename);
#endif // WIN_NT
}
2001-05-23 15:26:42 +02:00
#ifdef UNIX
2010-06-26 03:18:53 +02:00
bool SharedMemoryBase::mapFile(Arg::StatusVector& statusVector,
const TEXT* filename, ULONG length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m a p _ f i l e ( U N I X - m m a p )
*
**************************************
*
* Functional description
* Try to map a given file. If we are the first (i.e. only)
* process to map the file, call a given initialization
* routine (if given) or punt (leaving the file unmapped).
*
**************************************/
setHeader(NULL);
TEXT expanded_filename[MAXPATHLEN];
gds__prefix_lock(expanded_filename, filename);
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// make the complete filename for the init file this file is to be used as a
// master lock to eliminate possible race conditions with just a single file
// locking. The race condition is caused as the conversion of a EXCLUSIVE
// lock to a LCK_SHARED lock is not atomic
2001-05-23 15:26:42 +02:00
TEXT init_filename[MAXPATHLEN];
gds__prefix_lock(init_filename, INIT_FILE);
2001-05-23 15:26:42 +02:00
const bool trunc_flag = (length != 0);
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// open the init lock file
MutexLockGuard guard(openFdInit);
#ifdef USE_SYS5SEMAPHORE
if (fd_init < 0)
#else
int
#endif
fd_init = os_utils::openCreateSharedFile(init_filename, 0);
2009-06-23 15:26:12 +02:00
if (fd_init == -1)
{
error(statusVector, "open", errno);
return false;
2001-05-23 15:26:42 +02:00
}
2009-11-23 08:36:52 +01:00
// get an exclusive lock on the INIT file with blocking
FileLock initLock(statusVector, fd_init);
#ifdef USE_SYS5SEMAPHORE
initLock.setDtorMode(FileLock::OPENED);
#endif
if (!initLock.exclusive())
{
return false;
}
2009-11-23 08:36:52 +01:00
// init file is locked - no races possible later in this function
#ifdef USE_SYS5SEMAPHORE
if (fdSem < 0)
{
TEXT sem_filename[MAXPATHLEN];
gds__prefix_lock(sem_filename, SEM_FILE);
const int f = os_utils::openCreateSharedFile(sem_filename, 0);
2009-06-23 15:26:12 +02:00
if (f == -1)
{
error(statusVector, "open", errno);
return false;
}
void* sTab = mmap(0, sizeof(SemTable), PROT_READ | PROT_WRITE, MAP_SHARED, f, 0);
2009-06-23 15:26:12 +02:00
if ((U_IPTR) sTab == (U_IPTR) -1)
{
error(statusVector, "mmap", errno);
return false;
}
fdSem = f;
semTable = (SemTable*) sTab;
initCache();
2001-05-23 15:26:42 +02:00
}
fb_assert(semTable);
FileLock semLock(statusVector, fdSem, FileLock::OPENED);
if (semLock.tryExclusive())
{
semTable->init();
2001-05-23 15:26:42 +02:00
}
if (!semLock.shared())
{
return false;
}
2001-05-23 15:26:42 +02:00
#endif
2009-11-23 08:36:52 +01:00
// open the file to be inited
const int fd = os_utils::openCreateSharedFile(expanded_filename, 0);
2009-06-23 15:26:12 +02:00
if (fd == -1)
{
error(statusVector, "open", errno);
return false;
2001-05-23 15:26:42 +02:00
}
2009-11-23 08:36:52 +01:00
// create lock in order to have file autoclosed on error
FileLock mainLock(statusVector, fd);
2009-06-23 15:26:12 +02:00
if (length == 0)
{
2009-11-23 08:36:52 +01:00
// Get and use the existing length of the shared segment
struct stat file_stat;
2009-06-23 15:26:12 +02:00
if (fstat(fd, &file_stat) == -1)
{
error(statusVector, "fstat", errno);
return false;
2001-05-23 15:26:42 +02:00
}
length = file_stat.st_size;
if (length == 0)
{
// keep old text of message here - will be assigned a bit later
error(statusVector, "shmem_data->sh_mem_length_mapped is 0", 0);
return false;
}
}
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// map file to memory
void* const address = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2009-06-23 15:26:12 +02:00
if ((U_IPTR) address == (U_IPTR) -1)
{
error(statusVector, "mmap", errno);
return false;
2001-05-23 15:26:42 +02:00
}
#ifdef USE_SYS5SEMAPHORE
// register mapped file
// this class is needed to auto-unregister it in case of failure
class sfHolder
{
public:
2009-06-04 15:16:34 +02:00
explicit sfHolder(const SharedFile& sf) : pop(true)
{
SharedFile::push(sf);
}
2008-10-16 10:52:33 +02:00
void materialize()
{
pop = false;
}
~sfHolder()
{
if (pop)
{
SharedFile::pop();
}
}
private:
bool pop;
};
sfHolder holder(SharedFile(expanded_filename, address, length));
#endif
MemoryHeader* hdr = (MemoryHeader*) address;
setHeader(hdr);
sh_mem_length_mapped = length;
sh_mem_handle = fd;
strcpy(sh_mem_name, filename);
#if defined(HAVE_OBJECT_MAP) && (!defined(USE_SYS5SEMAPHORE))
sh_mem_mutex = (mtx*) mapObject(statusVector, OFFSET(MemoryHeader*, mhb_mutex), sizeof(mtx));
if (!sh_mem_mutex)
{
munmap(address, length);
setHeader(NULL);
return false;
}
#else
sh_mem_mutex = &hdr->mhb_mutex;
#endif
2009-11-23 08:36:52 +01:00
// Try to get an exclusive lock on the lock file. This will
// fail if somebody else has the exclusive or shared lock
2001-05-23 15:26:42 +02:00
if (mainLock.tryExclusive())
2001-05-23 15:26:42 +02:00
{
if (trunc_flag)
ftruncate(fd, length);
if (initialize(true))
2009-06-23 15:26:12 +02:00
{
int mtxState = ISC_mutex_init(sh_mem_mutex);
if (mtxState != 0)
{
mutexBug(mtxState, "ISC_init_mutex");
}
if (!mainLock.tryShared())
{
munmap(address, length);
setHeader(NULL);
return false;
}
2001-05-23 15:26:42 +02:00
}
}
2009-06-23 15:26:12 +02:00
else
{
if (initialize(false))
2009-06-23 15:26:12 +02:00
{
if (!mainLock.tryShared())
{
munmap(address, length);
setHeader(NULL);
return false;
}
2001-05-23 15:26:42 +02:00
}
}
2009-11-23 08:36:52 +01:00
// keep opened the shared file_decriptor
mainLock.setDtorMode(FileLock::LOCKED);
#ifdef USE_SYS5SEMAPHORE
// keep shared lock before last shared memory region unmapped
semLock.setDtorMode(FileLock::LOCKED);
++sharedCount;
// keep registered mapped file permanently
holder.materialize();
#endif
return true;
2001-05-23 15:26:42 +02:00
}
#endif // UNIX
#ifdef WIN_NT
2010-06-26 03:18:53 +02:00
bool SharedMemoryBase::mapFile(Arg::StatusVector& statusVector,
const TEXT* filename, ULONG length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m a p _ f i l e ( W I N _ N T )
*
**************************************
*
* Functional description
* Try to map a given file. If we are the first (i.e. only)
* process to map the file, call a given initialization
* routine (if given) or punt (leaving the file unmapped).
*
**************************************/
setHeader(NULL);
ISC_mutex_init(&sh_mem_winMutex, filename);
sh_mem_mutex = &sh_mem_winMutex;
HANDLE file_handle;
HANDLE event_handle = 0;
2001-05-23 15:26:42 +02:00
int retry_count = 0;
TEXT expanded_filename[MAXPATHLEN];
gds__prefix_lock(expanded_filename, filename);
const bool trunc_flag = (length != 0);
bool init_flag = false;
2009-11-23 08:36:52 +01:00
// retry to attach to mmapped file if the process initializing dies during initialization.
2001-05-23 15:26:42 +02:00
retry:
if (retry_count++ > 0)
THD_sleep(10);
2001-05-23 15:26:42 +02:00
file_handle = CreateFile(expanded_filename,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
DWORD err = GetLastError();
2009-06-23 15:26:12 +02:00
if (file_handle == INVALID_HANDLE_VALUE)
{
if (err == ERROR_SHARING_VIOLATION)
goto retry;
error(statusVector, "CreateFile", GetLastError());
return false;
2001-05-23 15:26:42 +02:00
}
// Check if file already exists
2001-05-23 15:26:42 +02:00
const bool file_exists = (err == ERROR_ALREADY_EXISTS);
2001-05-23 15:26:42 +02:00
// Create an event that can be used to determine if someone has already
// initialized shared memory.
2009-02-03 12:02:00 +01:00
TEXT object_name[MAXPATHLEN];
if (!make_object_name(object_name, sizeof(object_name), filename, "_event"))
{
error(statusVector, "make_object_name", GetLastError());
CloseHandle(file_handle);
return false;
}
2001-05-23 15:26:42 +02:00
if (!init_flag)
2009-06-23 15:26:12 +02:00
{
event_handle = CreateEvent(ISC_get_security_desc(), TRUE, FALSE, object_name);
if (!event_handle)
{
error(statusVector, "CreateEvent", GetLastError());
CloseHandle(file_handle);
return false;
}
2001-05-23 15:26:42 +02:00
init_flag = (GetLastError() != ERROR_ALREADY_EXISTS);
2001-05-23 15:26:42 +02:00
if (init_flag && false)
{
CloseHandle(event_handle);
CloseHandle(file_handle);
statusVector << Arg::Gds(isc_unavailable);
return false;
}
2001-05-23 15:26:42 +02:00
}
2009-06-23 15:26:12 +02:00
if (length == 0)
{
2009-11-23 08:36:52 +01:00
// Get and use the existing length of the shared segment
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if ((length = GetFileSize(file_handle, NULL)) == -1)
{
error(statusVector, "GetFileSize", GetLastError());
2001-05-23 15:26:42 +02:00
CloseHandle(event_handle);
CloseHandle(file_handle);
return false;
2001-05-23 15:26:42 +02:00
}
}
2009-11-23 08:36:52 +01:00
// All but the initializer will wait until the event is set. That
// is done after initialization is complete.
// Close the file and wait for the event to be set or time out.
// The file may be truncated.
2001-05-23 15:26:42 +02:00
CloseHandle(file_handle);
2009-06-23 15:26:12 +02:00
if (!init_flag)
{
2009-11-23 08:36:52 +01:00
// Wait for 10 seconds. Then retry
2001-05-23 15:26:42 +02:00
const DWORD ret_event = WaitForSingleObject(event_handle, 10000);
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// If we timed out, just retry. It is possible that the
// process doing the initialization died before setting the event.
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if (ret_event == WAIT_TIMEOUT)
{
2001-05-23 15:26:42 +02:00
CloseHandle(event_handle);
2009-06-23 15:26:12 +02:00
if (retry_count > 10)
{
error(statusVector, "WaitForSingleObject", 0);
return false;
2001-05-23 15:26:42 +02:00
}
goto retry;
}
}
DWORD fdw_create;
if (init_flag && file_exists && trunc_flag)
fdw_create = TRUNCATE_EXISTING;
2001-05-23 15:26:42 +02:00
else
fdw_create = OPEN_ALWAYS;
file_handle = CreateFile(expanded_filename,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
fdw_create,
FILE_ATTRIBUTE_NORMAL,
NULL);
2009-06-23 15:26:12 +02:00
if (file_handle == INVALID_HANDLE_VALUE)
{
const DWORD err = GetLastError();
if ((err == ERROR_SHARING_VIOLATION) || (err == ERROR_FILE_NOT_FOUND && fdw_create == TRUNCATE_EXISTING))
{
if (!init_flag) {
CloseHandle(event_handle);
}
goto retry;
}
2009-08-10 00:21:31 +02:00
if (err == ERROR_USER_MAPPED_FILE && init_flag && file_exists && trunc_flag)
statusVector << Arg::Gds(isc_instance_conflict);
else
error(statusVector, "CreateFile", err);
2001-05-23 15:26:42 +02:00
CloseHandle(event_handle);
return false;
2001-05-23 15:26:42 +02:00
}
if (!init_flag)
{
if ((GetLastError() != ERROR_ALREADY_EXISTS) || SetFilePointer(file_handle, 0, NULL, FILE_END) == 0)
{
CloseHandle(event_handle);
CloseHandle(file_handle);
goto retry;
}
}
2009-11-23 08:36:52 +01:00
// Create a file mapping object that will be used to make remapping possible.
// The current length of real mapped file and its name are saved in it.
2001-05-23 15:26:42 +02:00
if (!make_object_name(object_name, sizeof(object_name), filename, "_mapping"))
{
error(statusVector, "make_object_name", GetLastError());
CloseHandle(event_handle);
CloseHandle(file_handle);
return false;
}
2001-05-23 15:26:42 +02:00
HANDLE header_obj = CreateFileMapping(INVALID_HANDLE_VALUE,
ISC_get_security_desc(),
PAGE_READWRITE,
0, 2 * sizeof(ULONG),
object_name);
2008-10-21 06:25:49 +02:00
if (header_obj == NULL)
{
error(statusVector, "CreateFileMapping", GetLastError());
2001-05-23 15:26:42 +02:00
CloseHandle(event_handle);
CloseHandle(file_handle);
return false;
2001-05-23 15:26:42 +02:00
}
2008-06-26 12:43:57 +02:00
if (!init_flag && GetLastError() != ERROR_ALREADY_EXISTS)
{
2008-06-26 12:43:57 +02:00
// We have made header_obj but we are not initializing.
// Previous owner is closed and clear all header_data.
// One need to retry.
CloseHandle(header_obj);
CloseHandle(event_handle);
CloseHandle(file_handle);
goto retry;
}
2001-05-23 15:26:42 +02:00
ULONG* const header_address = (ULONG*) MapViewOfFile(header_obj, FILE_MAP_WRITE, 0, 0, 0);
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if (header_address == NULL)
{
error(statusVector, "MapViewOfFile", GetLastError());
2001-05-23 15:26:42 +02:00
CloseHandle(header_obj);
CloseHandle(event_handle);
CloseHandle(file_handle);
return false;
2001-05-23 15:26:42 +02:00
}
2009-11-23 08:36:52 +01:00
// Set or get the true length of the file depending on whether or not we are the first user.
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if (init_flag)
{
2001-05-23 15:26:42 +02:00
header_address[0] = length;
header_address[1] = 0;
}
else
length = header_address[0];
2009-11-23 08:36:52 +01:00
// Create the real file mapping object.
2001-05-23 15:26:42 +02:00
2009-08-09 10:14:30 +02:00
TEXT mapping_name[64]; // enough for int32 as text
sprintf(mapping_name, "_mapping_%"ULONGFORMAT, header_address[1]);
if (!make_object_name(object_name, sizeof(object_name), filename, mapping_name))
{
error(statusVector, "make_object_name", GetLastError());
UnmapViewOfFile(header_address);
CloseHandle(header_obj);
CloseHandle(event_handle);
CloseHandle(file_handle);
return false;
}
2001-05-23 15:26:42 +02:00
HANDLE file_obj = CreateFileMapping(file_handle,
ISC_get_security_desc(),
PAGE_READWRITE,
0, length,
object_name);
2009-06-23 15:26:12 +02:00
if (file_obj == NULL)
{
error(statusVector, "CreateFileMapping", GetLastError());
2001-05-23 15:26:42 +02:00
UnmapViewOfFile(header_address);
CloseHandle(header_obj);
CloseHandle(event_handle);
CloseHandle(file_handle);
return false;
2001-05-23 15:26:42 +02:00
}
UCHAR* const address = (UCHAR*) MapViewOfFile(file_obj, FILE_MAP_WRITE, 0, 0, 0);
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if (address == NULL)
{
error(statusVector, "MapViewOfFile", GetLastError());
2001-05-23 15:26:42 +02:00
CloseHandle(file_obj);
UnmapViewOfFile(header_address);
CloseHandle(header_obj);
CloseHandle(event_handle);
CloseHandle(file_handle);
return false;
2001-05-23 15:26:42 +02:00
}
MemoryHeader* hdr = (MemoryHeader*) address;
setHeader(hdr);
sh_mem_length_mapped = length;
if (!sh_mem_length_mapped)
{
error(statusVector, "sh_mem_length_mapped is 0", 0);
return false;
}
sh_mem_handle = file_handle;
sh_mem_object = file_obj;
sh_mem_interest = event_handle;
sh_mem_hdr_object = header_obj;
sh_mem_hdr_address = header_address;
strcpy(sh_mem_name, filename);
2001-05-23 15:26:42 +02:00
initialize(init_flag);
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if (init_flag)
{
2001-05-23 15:26:42 +02:00
FlushViewOfFile(address, 0);
DWORD err = 0;
if (SetFilePointer(sh_mem_handle, length, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
!SetEndOfFile(sh_mem_handle) ||
!FlushViewOfFile(address, 0))
{
err = GetLastError();
}
SetEvent(event_handle);
if (err)
{
2011-02-02 12:56:48 +01:00
error(statusVector, "SetFilePointer", err);
return false;
2001-05-23 15:26:42 +02:00
}
}
return true;
2001-05-23 15:26:42 +02:00
}
#endif
#ifdef HAVE_MMAP
UCHAR* SharedMemoryBase::mapObject(Arg::StatusVector& statusVector, ULONG object_offset, ULONG object_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2002-02-16 04:05:21 +01:00
* I S C _ m a p _ o b j e c t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Try to map an object given a file mapping.
*
**************************************/
2009-11-23 08:36:52 +01:00
// Get system page size as this is the unit of mapping.
2001-05-23 15:26:42 +02:00
#ifdef SOLARIS
const long ps = sysconf(_SC_PAGESIZE);
2009-06-23 15:26:12 +02:00
if (ps == -1)
{
error(statusVector, "sysconf", errno);
2001-05-23 15:26:42 +02:00
return NULL;
}
#else
const int ps = getpagesize();
2009-06-23 15:26:12 +02:00
if (ps == -1)
{
error(statusVector, "getpagesize", errno);
2001-05-23 15:26:42 +02:00
return NULL;
}
#endif
const ULONG page_size = (ULONG) ps;
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// Compute the start and end page-aligned offsets which contain the object being mapped.
2001-05-23 15:26:42 +02:00
const ULONG start = (object_offset / page_size) * page_size;
const ULONG end = FB_ALIGN(object_offset + object_length, page_size);
const ULONG length = end - start;
int fd = sh_mem_handle;
2001-05-23 15:26:42 +02:00
2008-12-20 20:57:43 +01:00
UCHAR* address = (UCHAR*) mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, start);
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if ((U_IPTR) address == (U_IPTR) -1)
{
error(statusVector, "mmap", errno);
2001-05-23 15:26:42 +02:00
return NULL;
}
2009-11-23 08:36:52 +01:00
// Return the virtual address of the mapped object.
2001-05-23 15:26:42 +02:00
IPC_TRACE(("ISC_map_object in %p to %p %p\n", shmem_data->sh_mem_address, address, address + length));
return address + (object_offset - start);
2001-05-23 15:26:42 +02:00
}
void SharedMemoryBase::unmapObject(Arg::StatusVector& statusVector, UCHAR** object_pointer, ULONG object_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2002-02-16 04:05:21 +01:00
* I S C _ u n m a p _ o b j e c t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Try to unmap an object given a file mapping.
* Zero the object pointer after a successful unmap.
*
**************************************/
2009-11-23 08:36:52 +01:00
// Get system page size as this is the unit of mapping.
2001-05-23 15:26:42 +02:00
#ifdef SOLARIS
const long ps = sysconf(_SC_PAGESIZE);
2009-06-23 15:26:12 +02:00
if (ps == -1)
{
error(statusVector, "sysconf", errno);
return;
2001-05-23 15:26:42 +02:00
}
#else
const int ps = getpagesize();
2009-06-23 15:26:12 +02:00
if (ps == -1)
{
error(statusVector, "getpagesize", errno);
return;
2001-05-23 15:26:42 +02:00
}
#endif
const size_t page_size = (ULONG) ps;
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
// Compute the start and end page-aligned addresses which contain the mapped object.
2001-05-23 15:26:42 +02:00
char* const start = (char*) ((U_IPTR) (*object_pointer) & ~(page_size - 1));
char* const end =
(char*) ((U_IPTR) ((*object_pointer + object_length) + (page_size - 1)) & ~(page_size - 1));
const size_t length = end - start;
2001-05-23 15:26:42 +02:00
if (munmap(start, length) == -1)
2009-06-23 15:26:12 +02:00
{
error(statusVector, "munmap", errno);
return; // false;
2001-05-23 15:26:42 +02:00
}
*object_pointer = NULL;
return; // true;
2001-05-23 15:26:42 +02:00
}
2010-02-04 16:17:35 +01:00
#endif // HAVE_MMAP
2001-05-23 15:26:42 +02:00
2008-01-16 10:07:24 +01:00
#ifdef WIN_NT
2010-06-26 03:18:53 +02:00
UCHAR* SharedMemoryBase::mapObject(Arg::StatusVector& statusVector,
ULONG object_offset,
ULONG object_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-01-16 10:07:24 +01:00
* I S C _ m a p _ o b j e c t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
2008-01-16 10:07:24 +01:00
* Try to map an object given a file mapping.
2001-05-23 15:26:42 +02:00
*
**************************************/
2008-01-16 10:07:24 +01:00
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);
const ULONG page_size = sys_info.dwAllocationGranularity;
2001-05-23 15:26:42 +02:00
2008-01-16 10:07:24 +01:00
// Compute the start and end page-aligned offsets which
// contain the object being mapped.
2001-05-23 15:26:42 +02:00
2009-06-07 14:54:57 +02:00
const ULONG start = (object_offset / page_size) * page_size;
const ULONG end = FB_ALIGN(object_offset + object_length, page_size);
const ULONG length = end - start;
const HANDLE handle = sh_mem_object;
2001-05-23 15:26:42 +02:00
2008-01-16 10:07:24 +01:00
UCHAR* address = (UCHAR*) MapViewOfFile(handle, FILE_MAP_WRITE, 0, start, length);
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if (address == NULL)
{
error(statusVector, "MapViewOfFile", GetLastError());
2008-01-16 10:07:24 +01:00
return NULL;
}
2001-05-23 15:26:42 +02:00
2008-01-16 10:07:24 +01:00
// Return the virtual address of the mapped object.
2001-05-23 15:26:42 +02:00
2008-01-16 10:07:24 +01:00
return (address + (object_offset - start));
2001-05-23 15:26:42 +02:00
}
2010-06-26 03:18:53 +02:00
void SharedMemoryBase::unmapObject(Arg::StatusVector& statusVector,
2010-06-30 10:55:16 +02:00
UCHAR** object_pointer, ULONG /*object_length*/)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-01-16 10:07:24 +01:00
* I S C _ u n m a p _ o b j e c t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
2008-01-16 10:07:24 +01:00
* Try to unmap an object given a file mapping.
* Zero the object pointer after a successful unmap.
2001-05-23 15:26:42 +02:00
*
**************************************/
2008-01-16 10:07:24 +01:00
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);
const size_t page_size = sys_info.dwAllocationGranularity;
2001-05-23 15:26:42 +02:00
2008-01-16 10:07:24 +01:00
// Compute the start and end page-aligned offsets which
// contain the object being mapped.
const UCHAR* start = (UCHAR*) ((U_IPTR) *object_pointer & ~(page_size - 1));
2009-04-29 03:52:49 +02:00
if (!UnmapViewOfFile(start))
2009-04-28 15:08:04 +02:00
{
error(statusVector, "UnmapViewOfFile", GetLastError());
2009-04-28 15:08:04 +02:00
return;
}
*object_pointer = NULL;
2001-05-23 15:26:42 +02:00
}
#endif
#ifdef USE_POSIX_THREADS
#ifdef USE_SYS5SEMAPHORE
int ISC_mutex_init(struct mtx* mutex)
{
/**************************************
*
* I S C _ m u t e x _ i n i t ( S Y S V )
*
**************************************
*
* Functional description
* Initialize a mutex.
*
**************************************/
if (!getSem5(mutex))
{
return FB_FAILURE;
}
union semun arg;
arg.val = 1;
int state = semctl(mutex->getId(), mutex->semNum, SETVAL, arg);
if (state == -1)
{
return errno;
}
return 0;
}
int ISC_mutex_lock(struct mtx* mutex)
{
/**************************************
*
* I S C _ m u t e x _ l o c k ( S Y S V )
*
**************************************
*
* Functional description
2009-04-11 07:47:13 +02:00
* Seize a mutex.
*
**************************************/
struct sembuf sop;
sop.sem_num = mutex->semNum;
sop.sem_op = -1;
sop.sem_flg = SEM_UNDO;
2009-06-23 15:26:12 +02:00
for (;;)
{
int state = semop(mutex->getId(), &sop, 1);
if (state != -1)
break;
if (!SYSCALL_INTERRUPTED(errno))
return errno;
}
return 0;
}
int ISC_mutex_lock_cond(struct mtx* mutex)
{
/**************************************
*
* I S C _ m u t e x _ l o c k _ c o n d ( S Y S V )
*
**************************************
*
* Functional description
2009-04-11 07:47:13 +02:00
* Conditionally seize a mutex.
*
**************************************/
struct sembuf sop;
sop.sem_num = mutex->semNum;
sop.sem_op = -1;
sop.sem_flg = SEM_UNDO | IPC_NOWAIT;
2009-06-23 15:26:12 +02:00
for (;;)
{
int state = semop(mutex->getId(), &sop, 1);
if (state != -1)
break;
if (!SYSCALL_INTERRUPTED(errno))
return errno;
}
return 0;
}
int ISC_mutex_unlock(struct mtx* mutex)
{
/**************************************
*
* I S C _ m u t e x _ u n l o c k ( S Y S V )
*
**************************************
*
* Functional description
* Release a mutex.
*
**************************************/
struct sembuf sop;
sop.sem_num = mutex->semNum;
sop.sem_op = 1;
sop.sem_flg = SEM_UNDO;
2009-06-23 15:26:12 +02:00
for (;;)
{
int state = semop(mutex->getId(), &sop, 1);
if (state != -1)
break;
if (!SYSCALL_INTERRUPTED(errno))
return errno;
}
return 0;
}
#else // not USE_SYS5SEMAPHORE
#if (defined(HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL) || defined(USE_ROBUST_MUTEX)) && defined(LINUX)
// glibc in linux does not conform to the posix standard. When there is no RT kernel,
// ENOTSUP is returned not by pthread_mutexattr_setprotocol(), but by
// pthread_mutex_init(). Here is a hack to deal with this broken error reporting.
#define BUGGY_LINUX_MUTEX
#endif
#ifdef BUGGY_LINUX_MUTEX
static volatile bool staticBugFlag = false;
#endif
int ISC_mutex_init(struct mtx* mutex)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ i n i t ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
* Initialize a mutex.
*
**************************************/
#ifdef BUGGY_LINUX_MUTEX
2009-07-15 21:45:57 +02:00
do
{
bool bugFlag = staticBugFlag;
#endif
2001-05-23 15:26:42 +02:00
pthread_mutexattr_t mattr;
PTHREAD_ERRNO(pthread_mutexattr_init(&mattr));
#ifdef PTHREAD_PROCESS_SHARED
PTHREAD_ERRNO(pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED));
#else
2009-04-11 21:41:01 +02:00
#error Your system must support PTHREAD_PROCESS_SHARED to use Firebird.
#endif
#ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
#ifdef BUGGY_LINUX_MUTEX
if (!bugFlag)
{
#endif
int protocolRc = pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
if (protocolRc && (protocolRc != ENOTSUP))
{
iscLogStatus("Pthread Error", (Arg::Gds(isc_sys_request) <<
"pthread_mutexattr_setprotocol" << Arg::Unix(protocolRc)).value());
}
#ifdef BUGGY_LINUX_MUTEX
}
#endif
#endif
#ifdef USE_ROBUST_MUTEX
#ifdef BUGGY_LINUX_MUTEX
if (!bugFlag)
{
#endif
LOG_PTHREAD_ERROR(pthread_mutexattr_setrobust_np(&mattr, PTHREAD_MUTEX_ROBUST_NP));
#ifdef BUGGY_LINUX_MUTEX
}
#endif
#endif
memset(mutex->mtx_mutex, 0, sizeof(pthread_mutex_t));
//int state = LOG_PTHREAD_ERROR(pthread_mutex_init(mutex->mtx_mutex, &mattr));
int state = pthread_mutex_init(mutex->mtx_mutex, &mattr);
2009-07-21 15:55:33 +02:00
if (state
#ifdef BUGGY_LINUX_MUTEX
2009-07-23 02:56:28 +02:00
&& (state != ENOTSUP || bugFlag)
2009-07-21 15:55:33 +02:00
#endif
)
{
iscLogStatus("Pthread Error", (Arg::Gds(isc_sys_request) <<
"pthread_mutex_init" << Arg::Unix(state)).value());
}
LOG_PTHREAD_ERROR(pthread_mutexattr_destroy(&mattr));
#ifdef BUGGY_LINUX_MUTEX
if (state == ENOTSUP && !bugFlag)
{
staticBugFlag = true;
continue;
}
return state; // To avoid declaring 'state' out of loop
} while (false);
2009-07-14 16:29:30 +02:00
return 0; // compiler warning silencer
#else
return state;
#endif
2001-05-23 15:26:42 +02:00
}
int ISC_mutex_lock(struct mtx* mutex)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ l o c k ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
2009-04-11 07:47:13 +02:00
* Seize a mutex.
2001-05-23 15:26:42 +02:00
*
**************************************/
int state = pthread_mutex_lock(mutex->mtx_mutex);
#ifdef USE_ROBUST_MUTEX
if (state == EOWNERDEAD)
{
// We always perform check for dead process
// Therefore may safely mark mutex as recovered
LOG_PTHREAD_ERROR(pthread_mutex_consistent_np(mutex->mtx_mutex));
state = 0;
}
#endif
return state;
2001-05-23 15:26:42 +02:00
}
int ISC_mutex_lock_cond(struct mtx* mutex)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ l o c k _ c o n d ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
2009-04-11 07:47:13 +02:00
* Conditionally seize a mutex.
2001-05-23 15:26:42 +02:00
*
**************************************/
return pthread_mutex_trylock(mutex->mtx_mutex);
}
int ISC_mutex_unlock(struct mtx* mutex)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ u n l o c k ( P O S I X _ T H R E A D S )
*
**************************************
*
* Functional description
* Release a mutex.
*
**************************************/
return pthread_mutex_unlock(mutex->mtx_mutex);
}
2008-10-11 04:42:01 +02:00
#endif // USE_SYS5SEMAPHORE
2008-10-11 04:42:01 +02:00
#endif // USE_POSIX_THREADS
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
static const LPCSTR FAST_MUTEX_EVT_NAME = "%s_FM_EVT";
static const LPCSTR FAST_MUTEX_MAP_NAME = "%s_FM_MAP";
static const int DEFAULT_INTERLOCKED_SPIN_COUNT = 0;
static const int DEFAULT_INTERLOCKED_SPIN_COUNT_SMP = 200;
static SLONG pid = 0;
2007-04-21 05:27:36 +02:00
typedef WINBASEAPI BOOL (WINAPI *pfnSwitchToThread) ();
2007-04-21 05:27:36 +02:00
static inline BOOL switchToThread()
{
static pfnSwitchToThread fnSwitchToThread = NULL;
static bool bInit = false;
if (!bInit)
{
HMODULE hLib = GetModuleHandle("kernel32.dll");
2011-05-10 03:12:14 +02:00
if (hLib)
fnSwitchToThread = (pfnSwitchToThread) GetProcAddress(hLib, "SwitchToThread");
2011-05-10 03:12:14 +02:00
bInit = true;
}
BOOL res = FALSE;
2011-05-10 03:12:14 +02:00
2008-10-21 06:25:49 +02:00
if (fnSwitchToThread)
{
const HANDLE hThread = GetCurrentThread();
SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
2009-04-11 21:41:01 +02:00
res = (*fnSwitchToThread)();
SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL);
}
return res;
}
2008-05-14 07:17:53 +02:00
// MinGW has the wrong declaration for the operating system function.
#if defined __GNUC__
2007-04-18 13:02:41 +02:00
// Cast away volatile
2007-03-01 11:35:11 +01:00
#define FIX_TYPE(arg) const_cast<LPLONG>(arg)
#else
#define FIX_TYPE(arg) arg
#endif
static inline void lockSharedSection(volatile FAST_MUTEX_SHARED_SECTION* lpSect, ULONG SpinCount)
{
while (InterlockedExchange(FIX_TYPE(&lpSect->lSpinLock), 1) != 0)
{
2008-10-21 06:25:49 +02:00
ULONG j = SpinCount;
while (j != 0)
{
if (lpSect->lSpinLock == 0)
goto next;
j--;
}
2007-04-21 05:27:36 +02:00
switchToThread();
next:;
}
}
static inline bool tryLockSharedSection(volatile FAST_MUTEX_SHARED_SECTION* lpSect)
{
return (InterlockedExchange(FIX_TYPE(&lpSect->lSpinLock), 1) == 0);
}
static inline void unlockSharedSection(volatile FAST_MUTEX_SHARED_SECTION* lpSect)
{
InterlockedExchange(FIX_TYPE(&lpSect->lSpinLock), 0);
}
static DWORD enterFastMutex(FAST_MUTEX* lpMutex, DWORD dwMilliseconds)
{
volatile FAST_MUTEX_SHARED_SECTION* lpSect = lpMutex->lpSharedInfo;
while (true)
{
2009-06-23 15:26:12 +02:00
if (dwMilliseconds == 0)
{
if (!tryLockSharedSection(lpSect))
return WAIT_TIMEOUT;
}
else {
lockSharedSection(lpSect, lpMutex->lSpinCount);
}
if (lpSect->lAvailable > 0)
{
lpSect->lAvailable--;
#ifdef _DEBUG
lpSect->dwThreadId = GetCurrentThreadId();
#endif
lpSect->lOwnerPID = pid;
unlockSharedSection(lpSect);
return WAIT_OBJECT_0;
}
#ifdef _DEBUG
if (lpSect->dwThreadId == GetCurrentThreadId())
DebugBreak();
#endif
if (dwMilliseconds == 0)
{
unlockSharedSection(lpSect);
return WAIT_TIMEOUT;
}
InterlockedIncrement(FIX_TYPE(&lpSect->lThreadsWaiting));
unlockSharedSection(lpSect);
2008-10-21 06:25:49 +02:00
// TODO actual timeout can be of any length
2011-04-02 06:50:25 +02:00
const DWORD tm = (dwMilliseconds == INFINITE || dwMilliseconds > 5000) ? 5000 : dwMilliseconds;
const DWORD dwResult = WaitForSingleObject(lpMutex->hEvent, tm);
InterlockedDecrement(FIX_TYPE(&lpSect->lThreadsWaiting));
2008-10-21 06:25:49 +02:00
if (dwMilliseconds != INFINITE)
dwMilliseconds -= tm;
// if (dwResult != WAIT_OBJECT_0)
// return dwResult;
if (dwResult == WAIT_OBJECT_0)
continue;
if (dwResult == WAIT_ABANDONED)
return dwResult;
if (dwResult == WAIT_TIMEOUT && !dwMilliseconds)
return dwResult;
lockSharedSection(lpSect, lpMutex->lSpinCount);
if (lpSect->lOwnerPID > 0 && !lpSect->lAvailable)
{
if (!ISC_check_process_existence(lpSect->lOwnerPID))
{
#ifdef DEBUG
gds__log("enterFastMutex: dead process detected, pid = %d", lpSect->lOwnerPID);
#endif
lpSect->lOwnerPID = 0;
lpSect->lAvailable++;
}
}
unlockSharedSection(lpSect);
}
}
static bool leaveFastMutex(FAST_MUTEX* lpMutex)
{
volatile FAST_MUTEX_SHARED_SECTION* lpSect = lpMutex->lpSharedInfo;
lockSharedSection(lpSect, lpMutex->lSpinCount);
if (lpSect->lAvailable >= 1)
{
unlockSharedSection(lpSect);
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
lpSect->lAvailable++;
if (lpSect->lThreadsWaiting)
SetEvent(lpMutex->hEvent);
lpSect->lOwnerPID = -pid;
unlockSharedSection(lpSect);
return true;
}
2007-03-01 11:35:11 +01:00
static inline void deleteFastMutex(FAST_MUTEX* lpMutex)
{
UnmapViewOfFile((FAST_MUTEX_SHARED_SECTION*)lpMutex->lpSharedInfo);
CloseHandle(lpMutex->hFileMap);
CloseHandle(lpMutex->hEvent);
}
static inline void setupMutex(FAST_MUTEX* lpMutex)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
if (si.dwNumberOfProcessors > 1)
lpMutex->lSpinCount = DEFAULT_INTERLOCKED_SPIN_COUNT_SMP;
else
lpMutex->lSpinCount = DEFAULT_INTERLOCKED_SPIN_COUNT;
}
2008-10-21 06:25:49 +02:00
static bool initializeFastMutex(FAST_MUTEX* lpMutex, LPSECURITY_ATTRIBUTES lpAttributes,
BOOL bInitialState, LPCSTR lpName)
{
if (pid == 0)
pid = GetCurrentProcessId();
LPCSTR name = lpName;
if (strlen(lpName) + strlen(FAST_MUTEX_EVT_NAME) - 2 >= MAXPATHLEN)
{
// this is the same error which CreateEvent will return for long name
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return false;
}
setupMutex(lpMutex);
2008-01-16 10:07:24 +01:00
char sz[MAXPATHLEN];
if (lpName)
{
sprintf(sz, FAST_MUTEX_EVT_NAME, lpName);
name = sz;
}
#ifdef DONT_USE_FAST_MUTEX
lpMutex->lpSharedInfo = NULL;
lpMutex->hEvent = CreateMutex(lpAttributes, bInitialState, name);
return (lpMutex->hEvent != NULL);
#else
lpMutex->hEvent = CreateEvent(lpAttributes, FALSE, FALSE, name);
2008-01-16 10:07:24 +01:00
DWORD dwLastError = GetLastError();
if (lpMutex->hEvent)
{
if (lpName)
sprintf(sz, FAST_MUTEX_MAP_NAME, lpName);
lpMutex->hFileMap = CreateFileMapping(
2008-10-21 06:25:49 +02:00
INVALID_HANDLE_VALUE,
lpAttributes,
PAGE_READWRITE,
0,
sizeof(FAST_MUTEX_SHARED_SECTION),
name);
dwLastError = GetLastError();
if (lpMutex->hFileMap)
{
2008-10-21 06:25:49 +02:00
lpMutex->lpSharedInfo = (FAST_MUTEX_SHARED_SECTION*)
MapViewOfFile(lpMutex->hFileMap, FILE_MAP_WRITE, 0, 0, 0);
if (lpMutex->lpSharedInfo)
{
if (dwLastError != ERROR_ALREADY_EXISTS)
{
lpMutex->lpSharedInfo->lSpinLock = 0;
lpMutex->lpSharedInfo->lThreadsWaiting = 0;
lpMutex->lpSharedInfo->lAvailable = bInitialState ? 0 : 1;
lpMutex->lpSharedInfo->lOwnerPID = bInitialState ? pid : 0;
2007-03-01 11:35:11 +01:00
InterlockedExchange(FIX_TYPE(&lpMutex->lpSharedInfo->fInitialized), 1);
}
else
{
2008-10-21 06:25:49 +02:00
while (!lpMutex->lpSharedInfo->fInitialized)
2007-04-21 05:27:36 +02:00
switchToThread();
}
SetLastError(dwLastError);
return true;
}
CloseHandle(lpMutex->hFileMap);
}
CloseHandle(lpMutex->hEvent);
}
SetLastError(dwLastError);
return false;
#endif // DONT_USE_FAST_MUTEX
}
#ifdef NOT_USED_OR_REPLACED
static bool openFastMutex(FAST_MUTEX* lpMutex, DWORD DesiredAccess, LPCSTR lpName)
{
LPCSTR name = lpName;
if (strlen(lpName) + strlen(FAST_MUTEX_EVT_NAME) - 2 >= MAXPATHLEN)
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return false;
}
setupMutex(lpMutex);
2008-01-16 10:07:24 +01:00
char sz[MAXPATHLEN];
if (lpName)
{
sprintf(sz, FAST_MUTEX_EVT_NAME, lpName);
name = sz;
}
2008-10-21 06:25:49 +02:00
lpMutex->hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, name);
2008-10-21 06:25:49 +02:00
2008-01-16 10:07:24 +01:00
DWORD dwLastError = GetLastError();
2008-10-21 06:25:49 +02:00
if (lpMutex->hEvent)
{
if (lpName)
sprintf(sz, FAST_MUTEX_MAP_NAME, lpName);
2008-10-21 06:25:49 +02:00
2008-12-20 09:12:19 +01:00
lpMutex->hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name);
2008-10-21 06:25:49 +02:00
dwLastError = GetLastError();
2008-10-21 06:25:49 +02:00
if (lpMutex->hFileMap)
{
2008-10-21 06:25:49 +02:00
lpMutex->lpSharedInfo = (FAST_MUTEX_SHARED_SECTION*)
MapViewOfFile(lpMutex->hFileMap, FILE_MAP_WRITE, 0, 0, 0);
2008-10-21 06:25:49 +02:00
if (lpMutex->lpSharedInfo)
return true;
CloseHandle(lpMutex->hFileMap);
}
CloseHandle(lpMutex->hEvent);
}
SetLastError(dwLastError);
return false;
}
#endif
static inline void setFastMutexSpinCount(FAST_MUTEX* lpMutex, ULONG SpinCount)
{
lpMutex->lSpinCount = SpinCount;
}
int ISC_mutex_init(struct mtx* mutex, const TEXT* mutex_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ i n i t ( W I N _ N T )
*
**************************************
*
* Functional description
* Initialize a mutex.
*
**************************************/
char name_buffer[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
if (!make_object_name(name_buffer, sizeof(name_buffer), mutex_name, "_mutex"))
{
return FB_FAILURE;
}
2001-05-23 15:26:42 +02:00
if (initializeFastMutex(&mutex->mtx_fast, ISC_get_security_desc(), FALSE, name_buffer))
return FB_SUCCESS;
fb_assert(GetLastError() != 0);
return GetLastError();
}
void ISC_mutex_fini(struct mtx *mutex)
{
/**************************************
*
* m u t e x _ f i n i ( W I N _ N T )
*
**************************************
*
* Functional description
* Destroy a mutex.
*
**************************************/
if (mutex->mtx_fast.lpSharedInfo)
deleteFastMutex(&mutex->mtx_fast);
2001-05-23 15:26:42 +02:00
}
int ISC_mutex_lock(struct mtx* mutex)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ l o c k ( W I N _ N T )
*
**************************************
*
* Functional description
2009-04-11 07:47:13 +02:00
* Seize a mutex.
2001-05-23 15:26:42 +02:00
*
**************************************/
const DWORD status = (mutex->mtx_fast.lpSharedInfo) ?
enterFastMutex(&mutex->mtx_fast, INFINITE) :
2008-12-20 09:12:19 +01:00
WaitForSingleObject(mutex->mtx_fast.hEvent, INFINITE);
2008-10-16 10:52:33 +02:00
return (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) ? FB_SUCCESS : FB_FAILURE;
2001-05-23 15:26:42 +02:00
}
int ISC_mutex_lock_cond(struct mtx* mutex)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ l o c k _ c o n d ( W I N _ N T )
*
**************************************
*
* Functional description
2009-04-11 07:47:13 +02:00
* Conditionally seize a mutex.
2001-05-23 15:26:42 +02:00
*
**************************************/
2008-10-21 06:25:49 +02:00
const DWORD status = (mutex->mtx_fast.lpSharedInfo) ?
2008-12-20 09:12:19 +01:00
enterFastMutex(&mutex->mtx_fast, 0) : WaitForSingleObject(mutex->mtx_fast.hEvent, 0L);
2008-10-16 10:52:33 +02:00
return (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) ? FB_SUCCESS : FB_FAILURE;
2001-05-23 15:26:42 +02:00
}
int ISC_mutex_unlock(struct mtx* mutex)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ m u t e x _ u n l o c k ( W I N _ N T )
*
**************************************
*
* Functional description
* Release a mutex.
*
**************************************/
if (mutex->mtx_fast.lpSharedInfo) {
return !leaveFastMutex(&mutex->mtx_fast);
}
2008-01-16 10:07:24 +01:00
return !ReleaseMutex(mutex->mtx_fast.hEvent);
2001-05-23 15:26:42 +02:00
}
void ISC_mutex_set_spin_count (struct mtx *mutex, ULONG spins)
{
2008-10-21 06:25:49 +02:00
if (mutex->mtx_fast.lpSharedInfo)
setFastMutexSpinCount(&mutex->mtx_fast, spins);
}
#endif // WIN_NT
2001-05-23 15:26:42 +02:00
#ifdef UNIX
#ifdef HAVE_MMAP
2001-05-23 15:26:42 +02:00
#define ISC_REMAP_FILE_DEFINED
bool SharedMemoryBase::remapFile(Arg::StatusVector& statusVector, ULONG new_length, bool flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ r e m a p _ f i l e ( U N I X - m m a p )
*
**************************************
*
* Functional description
* Try to re-map a given file.
*
**************************************/
if (!new_length)
{
error(statusVector, "Zero new_length is requested", 0);
return false;
}
2001-05-23 15:26:42 +02:00
if (flag)
ftruncate(sh_mem_handle, new_length);
2001-05-23 15:26:42 +02:00
MemoryHeader* const address = (MemoryHeader*)
mmap(0, new_length, PROT_READ | PROT_WRITE, MAP_SHARED, sh_mem_handle, 0);
if ((U_IPTR) address == (U_IPTR) -1)
{
error(statusVector, "mmap() failed", errno);
return false;
}
2001-05-23 15:26:42 +02:00
2010-06-26 03:18:53 +02:00
munmap((char*) getHeader(), sh_mem_length_mapped);
2001-05-23 15:26:42 +02:00
#ifdef USE_SYS5SEMAPHORE
SharedFile::remap(getHeader(), address, new_length);
#endif
IPC_TRACE(("ISC_remap_file %p to %p %d\n", getHeader(), address, new_length));
setHeader(address);
sh_mem_length_mapped = new_length;
2001-05-23 15:26:42 +02:00
#if (!defined(HAVE_OBJECT_MAP)) || defined(USE_SYS5SEMAPHORE)
sh_mem_mutex = &getHeader()->mhb_mutex;
#endif
2001-05-23 15:26:42 +02:00
return address;
}
2010-02-04 16:17:35 +01:00
#endif // HAVE_MMAP
#endif // UNIX
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
#define ISC_REMAP_FILE_DEFINED
2010-06-26 03:18:53 +02:00
bool SharedMemoryBase::remapFile(Arg::StatusVector& statusVector,
ULONG new_length, bool flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ r e m a p _ f i l e ( W I N _ N T )
*
**************************************
*
* Functional description
* Try to re-map a given file.
*
**************************************/
if (flag)
2008-10-24 08:22:55 +02:00
{
if (SetFilePointer(sh_mem_handle, new_length, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
!SetEndOfFile(sh_mem_handle) ||
!FlushViewOfFile(getHeader(), 0))
{
error(statusVector, "SetFilePointer", GetLastError());
2001-05-23 15:26:42 +02:00
return NULL;
}
2008-10-24 08:22:55 +02:00
}
2001-05-23 15:26:42 +02:00
2009-11-23 08:36:52 +01:00
/* If the remap file exists, remap does not occur correctly.
* The file number is local to the process and when it is
* incremented and a new filename is created, that file may
* already exist. In that case, the file is not expanded.
* This will happen when the file is expanded more than once
* by concurrently running processes.
*
* The problem will be fixed by making sure that a new file name
* is generated with the mapped file is created.
*/
2001-05-23 15:26:42 +02:00
HANDLE file_obj = NULL;
2009-06-23 15:26:12 +02:00
while (true)
{
TEXT mapping_name[64]; // enough for int32 as text
sprintf(mapping_name, "_mapping_%"ULONGFORMAT, sh_mem_hdr_address[1] + 1);
TEXT object_name[MAXPATHLEN];
if (!make_object_name(object_name, sizeof(object_name), sh_mem_name, mapping_name))
break;
2001-05-23 15:26:42 +02:00
file_obj = CreateFileMapping(sh_mem_handle,
ISC_get_security_desc(),
PAGE_READWRITE,
0, new_length,
object_name);
2001-05-23 15:26:42 +02:00
2009-08-10 00:21:31 +02:00
if (!(GetLastError() == ERROR_ALREADY_EXISTS && flag))
2001-05-23 15:26:42 +02:00
break;
CloseHandle(file_obj);
file_obj = NULL;
sh_mem_hdr_address[1]++;
2001-05-23 15:26:42 +02:00
}
2009-06-23 15:26:12 +02:00
if (file_obj == NULL)
{
error(statusVector, "CreateFileMapping", GetLastError());
2001-05-23 15:26:42 +02:00
return NULL;
}
MemoryHeader* const address = (MemoryHeader*) MapViewOfFile(file_obj, FILE_MAP_WRITE, 0, 0, 0);
2001-05-23 15:26:42 +02:00
2009-06-23 15:26:12 +02:00
if (address == NULL)
{
error(statusVector, "MapViewOfFile", GetLastError());
2001-05-23 15:26:42 +02:00
CloseHandle(file_obj);
return NULL;
}
2009-06-23 15:26:12 +02:00
if (flag)
{
sh_mem_hdr_address[0] = new_length;
sh_mem_hdr_address[1]++;
2001-05-23 15:26:42 +02:00
}
UnmapViewOfFile(getHeader());
CloseHandle(sh_mem_object);
2001-05-23 15:26:42 +02:00
setHeader(address);
sh_mem_length_mapped = new_length;
sh_mem_object = file_obj;
2001-05-23 15:26:42 +02:00
if (!sh_mem_length_mapped)
{
error(statusVector, "sh_mem_length_mapped is 0", 0);
return NULL;
}
return (address);
2001-05-23 15:26:42 +02:00
}
#endif
#ifndef ISC_REMAP_FILE_DEFINED
2010-06-26 03:18:53 +02:00
bool SharedMemoryBase::remapFile(Arg::StatusVector& statusVector, ULONG, bool)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ r e m a p _ f i l e ( G E N E R I C )
*
**************************************
*
* Functional description
* Try to re-map a given file.
*
**************************************/
2010-06-26 03:18:53 +02:00
statusVector << Arg::Gds(isc_unavailable) <<
Arg::Gds(isc_random) << "SharedMemory::remapFile";
2001-05-23 15:26:42 +02:00
return NULL;
}
#endif
#ifdef UNIX
void SharedMemoryBase::unmapFile(Arg::StatusVector& statusVector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ u n m a p _ f i l e ( U N I X - m m a p )
*
**************************************
*
* Functional description
* Unmap a given file.
2001-05-23 15:26:42 +02:00
*
**************************************/
#ifdef USE_SYS5SEMAPHORE
// Lock init file.
FileLock initLock(statusVector, fd_init, FileLock::OPENED);
if (!initLock.exclusive())
{
2009-12-06 03:28:00 +01:00
iscLogStatus("ISC_unmap_file failed to lock init file", statusVector.value());
}
else
{
SharedFile* sf = SharedFile::locate(getHeader());
FileLock lock(statusVector, sh_mem_handle);
lock.setLevel(FileLock::LCK_SHARED);
semTable->cleanup(sf->getNum(), lock.tryExclusive());
SharedFile::remove(getHeader());
}
--sharedCount;
#endif
2001-05-23 15:26:42 +02:00
#if defined(HAVE_OBJECT_MAP) && (!defined(USE_SYS5SEMAPHORE))
unmapObject(statusVector, (UCHAR**) &sh_mem_mutex, sizeof(mtx));
#endif
2001-05-23 15:26:42 +02:00
munmap((char *) getHeader(), sh_mem_length_mapped);
setHeader(NULL);
close(sh_mem_handle);
2001-05-23 15:26:42 +02:00
}
#endif
#ifdef WIN_NT
void SharedMemoryBase::unmapFile(Arg::StatusVector& statusVector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S C _ u n m a p _ f i l e ( W I N _ N T )
*
**************************************
*
* Functional description
* Detach from the shared memory.
2001-05-23 15:26:42 +02:00
*
**************************************/
CloseHandle(sh_mem_interest);
if (!UnmapViewOfFile(getHeader()))
2009-04-28 15:08:04 +02:00
{
error(statusVector, "UnmapViewOfFile", GetLastError());
2009-04-28 15:08:04 +02:00
return;
}
CloseHandle(sh_mem_object);
2008-10-21 06:25:49 +02:00
CloseHandle(sh_mem_handle);
if (!UnmapViewOfFile(sh_mem_hdr_address))
2009-04-28 15:08:04 +02:00
{
error(statusVector, "UnmapViewOfFile", GetLastError());
2009-04-28 15:08:04 +02:00
return;
}
CloseHandle(sh_mem_hdr_object);
TEXT expanded_filename[MAXPATHLEN];
gds__prefix_lock(expanded_filename, sh_mem_name);
// Delete file only if it is not used by anyone else
HANDLE hFile = CreateFile(expanded_filename,
DELETE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
ISC_mutex_fini(&sh_mem_winMutex);
sh_mem_mutex = NULL;
setHeader(NULL);
2001-05-23 15:26:42 +02:00
}
#endif
static void error(Arg::StatusVector& statusVector, const TEXT* string, ISC_STATUS status)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e r r o r
*
**************************************
*
* Functional description
* We've encountered an error, report it.
*
**************************************/
statusVector << Arg::Gds(isc_sys_request) << Arg::Str(string) << SYS_ERR(status);
statusVector.makePermanent();
2001-05-23 15:26:42 +02:00
}
#ifdef USE_SYS5SEMAPHORE
static SLONG create_semaphores(Arg::StatusVector& statusVector, SLONG key, int semaphores)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c r e a t e _ s e m a p h o r e s ( U N I X )
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Create or find a block of semaphores.
2001-05-23 15:26:42 +02:00
*
**************************************/
while (true)
{
// Try to open existing semaphore set
2009-06-04 15:16:34 +02:00
SLONG semid = semget(key, 0, 0);
2009-06-23 15:26:12 +02:00
if (semid == -1)
{
if (errno != ENOENT)
{
error(statusVector, "semget", errno);
return -1;
}
2008-10-21 06:25:49 +02:00
}
else
{
union semun arg;
2008-10-21 06:25:49 +02:00
semid_ds buf;
arg.buf = &buf;
// Get number of semaphores in opened set
2009-06-23 15:26:12 +02:00
if (semctl(semid, 0, IPC_STAT, arg) == -1)
{
error(statusVector, "semctl", errno);
return -1;
}
if ((int) buf.sem_nsems >= semaphores)
return semid;
// Number of semaphores in existing set is too small. Discard it.
2009-06-23 15:26:12 +02:00
if (semctl(semid, 0, IPC_RMID) == -1)
{
error(statusVector, "semctl", errno);
return -1;
}
}
2008-10-21 06:25:49 +02:00
// Try to create new semaphore set
semid = semget(key, semaphores, IPC_CREAT | IPC_EXCL | PRIV);
if (semid != -1)
{
// We want to limit access to semaphores, created here
// Reasonable access rights to them - exactly like security database has
const char* secDb = Config::getDefaultConfig()->getSecurityDatabase();
struct stat st;
if (stat(secDb, &st) == 0)
{
union semun arg;
semid_ds ds;
arg.buf = &ds;
ds.sem_perm.uid = geteuid() == 0 ? st.st_uid : geteuid();
ds.sem_perm.gid = st.st_gid;
ds.sem_perm.mode = st.st_mode;
semctl(semid, 0, IPC_SET, arg);
}
return semid;
2001-05-23 15:26:42 +02:00
}
2008-10-16 10:52:33 +02:00
2009-06-23 15:26:12 +02:00
if (errno != EEXIST)
{
error(statusVector, "semget", errno);
2008-10-16 10:52:33 +02:00
return -1;
2001-05-23 15:26:42 +02:00
}
}
}
2001-05-23 15:26:42 +02:00
2010-02-04 16:17:35 +01:00
#endif // USE_SYS5SEMAPHORE
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
2008-12-20 09:12:19 +01:00
static bool make_object_name(TEXT* buffer, size_t bufsize,
const TEXT* object_name,
const TEXT* object_type)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m a k e _ o b j e c t _ n a m e ( W I N _ N T )
*
**************************************
*
* Functional description
* Create an object name from a name and type.
* Also replace the file separator with "_".
*
**************************************/
char hostname[64];
const int rc = snprintf(buffer, bufsize, object_name, ISC_get_host(hostname, sizeof(hostname)));
2009-09-03 13:17:46 +02:00
if (size_t(rc) == bufsize || rc <= 0)
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return false;
}
char& limit = buffer[bufsize - 1];
limit = 0;
2008-10-21 06:25:49 +02:00
char* p;
char c;
2001-05-23 15:26:42 +02:00
for (p = buffer; c = *p; p++)
2008-04-21 16:02:47 +02:00
{
2001-05-23 15:26:42 +02:00
if (c == '/' || c == '\\' || c == ':')
*p = '_';
2008-04-21 16:02:47 +02:00
}
// We either append the full object type or produce failure.
if (p >= &limit || p + strlen(object_type) > &limit)
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return false;
}
2001-05-23 15:26:42 +02:00
strcpy(p, object_type);
// hvlad: windows file systems use case-insensitive file names
2008-10-21 06:25:49 +02:00
// while kernel objects such as events use case-sensitive names.
// Since we use root directory as part of kernel objects names
// we must use lower (or upper) register for object name to avoid
// misunderstanding between processes
strlwr(buffer);
// CVC: I'm not convinced that if this call has no space to put the prefix,
// we can ignore that fact, hence I changed that signature, too.
if (!fb_utils::prefix_kernel_object_name(buffer, bufsize))
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return false;
}
return true;
2001-05-23 15:26:42 +02:00
}
#endif // WIN_NT
void SharedMemoryBase::mutexLock()
{
int state = ISC_mutex_lock(sh_mem_mutex);
if (state != 0)
{
mutexBug(state, "ISC_mutex_lock");
}
}
bool SharedMemoryBase::mutexLockCond()
{
return ISC_mutex_lock_cond(sh_mem_mutex) == 0;
}
void SharedMemoryBase::mutexUnlock()
{
int state = ISC_mutex_unlock(sh_mem_mutex);
if (state != 0)
{
mutexBug(state, "ISC_mutex_unlock");
}
}
SharedMemoryBase::SharedMemoryBase()
2010-06-26 03:18:53 +02:00
: sh_mem_mutex(0), sh_mem_length_mapped(0),
#ifdef WIN_NT
sh_mem_handle(0), sh_mem_object(0), sh_mem_interest(0), sh_mem_hdr_object(0),
sh_mem_hdr_address(0)
#else
sh_mem_handle(-1)
#endif
{
sh_mem_name[0] = '\0';
}
SharedMemoryBase::~SharedMemoryBase()
{
}
2010-06-26 03:18:53 +02:00
void SharedMemoryBase::logError(const char* text, const Arg::StatusVector& status)
{
iscLogStatus(text, status.value());
}