8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-01 10:03:04 +01:00
firebird-mirror/src/common/classes/semaphore.h

543 lines
11 KiB
C
Raw Normal View History

/*
* PROGRAM: Client/Server Common Code
* MODULE: semaphore.h
* DESCRIPTION: Semaphore lock
*
* The contents of this file are subject to the Initial
* Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* 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 Nickolay Samofatov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2004 Nickolay Samofatov <nickolay@broadviewsoftware.com>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
*
*/
#ifndef CLASSES_SEMAPHORE_H
#define CLASSES_SEMAPHORE_H
2007-12-19 15:35:52 +01:00
#include "../jrd/gdsassert.h"
2008-01-16 07:24:06 +01:00
#ifdef WIN_NT
// Note: Windows does not need signal safe version of the class
#include <windows.h>
#include <limits.h>
namespace Firebird
{
class MemoryPool;
class Semaphore
{
private:
HANDLE hSemaphore;
void init()
{
hSemaphore = CreateSemaphore(NULL, 0 /*initial count*/, INT_MAX, NULL);
if (hSemaphore == NULL)
system_call_failed::raise("CreateSemaphore");
}
public:
Semaphore() { init(); }
explicit Semaphore(class MemoryPool&) { init(); }
~Semaphore()
{
if (hSemaphore && !CloseHandle(hSemaphore))
system_call_failed::raise("CloseHandle");
}
bool tryEnter(int seconds = 0, int milliseconds = 0)
{
milliseconds += seconds * 1000;
DWORD result = WaitForSingleObject(
hSemaphore, milliseconds >= 0 ? milliseconds : INFINITE);
if (result == WAIT_FAILED)
system_call_failed::raise("WaitForSingleObject");
return result != WAIT_TIMEOUT;
}
void release(SLONG count = 1)
{
if (!ReleaseSemaphore(hSemaphore, count, NULL))
system_call_failed::raise("ReleaseSemaphore");
}
};
2007-11-12 16:18:49 +01:00
} // namespace Firebird
2008-01-16 07:24:06 +01:00
#else // WIN_NT
2007-11-15 12:31:56 +01:00
2007-11-12 16:18:49 +01:00
#ifdef HAVE_SEMAPHORE_H
#include <semaphore.h>
#include <errno.h>
2007-12-19 15:35:52 +01:00
#include <time.h>
2007-12-07 13:19:37 +01:00
#ifndef WORKING_SEM_INIT
#include <fcntl.h>
2007-12-19 15:35:52 +01:00
#if defined(DARWIN)
#ifdef SUPERSERVER
#define MIXED_SEMAPHORE_AND_FILE_HANDLE
2007-12-07 13:19:37 +01:00
#endif
2007-12-19 15:35:52 +01:00
#endif
2007-12-22 01:53:39 +01:00
#endif // WORKING_SEM_INIT
2007-11-12 16:18:49 +01:00
namespace Firebird
{
2007-12-07 13:19:37 +01:00
#ifndef WORKING_SEM_INIT
static const char* semName = "/firebird_temp_sem";
#endif
2007-11-12 16:18:49 +01:00
class SignalSafeSemaphore
{
2007-11-12 16:18:49 +01:00
private:
2007-12-07 13:19:37 +01:00
#ifdef WORKING_SEM_INIT
sem_t sem[1];
#else
sem_t* sem;
2007-12-19 15:35:52 +01:00
#ifdef MIXED_SEMAPHORE_AND_FILE_HANDLE
static bool divorceDone;
static SignalSafeSemaphore* initialList;
void linkToInitialList();
SignalSafeSemaphore* next;
2007-12-07 13:19:37 +01:00
#endif
2007-12-22 01:53:39 +01:00
#endif // WORKING_SEM_INIT
void init()
{
2007-12-07 13:19:37 +01:00
#ifdef WORKING_SEM_INIT
if (sem_init(sem, 0, 0) == -1) {
2007-11-12 16:18:49 +01:00
system_call_failed::raise("sem_init");
}
2007-12-07 13:19:37 +01:00
#else
sem = sem_open(semName, O_CREAT | O_EXCL, 0700, 0);
#if defined(DARWIN) && !defined(DARWIN64)
2007-12-22 01:53:39 +01:00
if (sem == (sem_t*) SEM_FAILED) {
2007-12-19 15:35:52 +01:00
#else
2007-12-07 13:19:37 +01:00
if (sem == SEM_FAILED) {
2007-12-19 15:35:52 +01:00
#endif
2007-12-07 13:19:37 +01:00
system_call_failed::raise("sem_open");
}
sem_unlink(semName);
2007-12-19 15:35:52 +01:00
#ifdef MIXED_SEMAPHORE_AND_FILE_HANDLE
linkToInitialList();
#endif
2007-12-07 13:19:37 +01:00
#endif
2007-11-12 16:18:49 +01:00
}
public:
SignalSafeSemaphore() { init(); }
explicit SignalSafeSemaphore(class MemoryPool&) { init(); }
#ifdef MIXED_SEMAPHORE_AND_FILE_HANDLE
static bool checkHandle(int n);
#endif
~SignalSafeSemaphore()
{
2007-12-07 13:19:37 +01:00
#ifdef WORKING_SEM_INIT
if (sem_destroy(sem) == -1) {
2007-11-12 16:18:49 +01:00
system_call_failed::raise("sem_destroy");
}
2007-12-07 13:19:37 +01:00
#else
if (sem_close(sem) == -1) {
system_call_failed::raise("sem_close");
}
#endif
2007-11-12 16:18:49 +01:00
}
void enter()
{
2007-11-12 16:18:49 +01:00
do {
2007-12-07 13:19:37 +01:00
if (sem_wait(sem) != -1)
2007-11-12 16:18:49 +01:00
return;
} while (errno == EINTR);
system_call_failed::raise("semaphore.h: enter: sem_wait()");
}
void release(SLONG count = 1)
{
2007-11-12 16:18:49 +01:00
for (int i = 0; i < count; i++)
2007-12-28 01:14:00 +01:00
{
2007-12-07 13:50:43 +01:00
if (sem_post(sem) == -1)
2007-12-27 11:55:58 +01:00
{
2007-11-12 16:18:49 +01:00
system_call_failed::raise("semaphore.h: release: sem_post()");
2007-12-27 11:55:58 +01:00
}
2007-12-28 01:14:00 +01:00
}
2007-11-12 16:18:49 +01:00
}
#ifdef HAVE_SEM_TIMEDWAIT
2007-11-17 01:38:16 +01:00
// In case when sem_timedwait() is implemented by host OS,
// class SignalSafeSemaphore may have this function:
bool tryEnter(int seconds = 0, int milliseconds = 0)
{
milliseconds += seconds * 1000;
2007-11-12 16:18:49 +01:00
// Return true in case of success
if (milliseconds == 0)
{
2007-11-12 16:18:49 +01:00
// Instant try
do {
2007-12-07 13:19:37 +01:00
if (sem_trywait(sem) != -1)
2007-11-12 16:18:49 +01:00
return true;
} while (errno == EINTR);
if (errno == EAGAIN)
return false;
system_call_failed::raise("sem_trywait");
}
if (milliseconds < 0)
{
2007-11-12 16:18:49 +01:00
// Unlimited wait, like enter()
do {
2007-12-07 13:19:37 +01:00
if (sem_wait(sem) != -1)
2007-11-12 16:18:49 +01:00
return true;
} while (errno == EINTR);
system_call_failed::raise("sem_wait");
}
// Wait with timeout
struct timespec timeout;
timeout.tv_sec = time(NULL) + milliseconds / 1000;
timeout.tv_nsec = (milliseconds % 1000) * 1000;
2007-11-12 16:18:49 +01:00
int errcode = 0;
do {
2007-12-07 13:19:37 +01:00
int rc = sem_timedwait(sem, &timeout);
2007-11-12 16:18:49 +01:00
if (rc == 0)
return true;
// fix for CORE-988, also please see
// http://carcino.gen.nz/tech/linux/glibc_sem_timedwait_errors.php
errcode = rc > 0 ? rc : errno;
} while (errcode == EINTR);
if (errcode == ETIMEDOUT) {
return false;
}
system_call_failed::raise("sem_timedwait", errcode);
return false; // avoid warnings
}
2008-01-16 07:24:06 +01:00
#endif // HAVE_SEM_TIMEDWAIT
2007-11-12 16:18:49 +01:00
};
#ifdef HAVE_SEM_TIMEDWAIT
// In case when sem_timedwait() is implemented by host OS,
// SignalSafeSemaphore and Semaphore are just the same
typedef SignalSafeSemaphore Semaphore;
2008-01-16 07:24:06 +01:00
#endif // HAVE_SEM_TIMEDWAIT
} // namespace Firebird
2008-01-16 07:24:06 +01:00
#endif // HAVE_SEMAPHORE_H
2007-11-12 16:18:49 +01:00
#ifndef HAVE_SEM_TIMEDWAIT
// Should implement Semaphore independent from SignalSafeSemaphore.
2007-11-12 16:18:49 +01:00
// In the worst case no SignalSafeSemaphore at all (and no SS for that platform).
2007-12-07 13:19:37 +01:00
#if defined(HAVE_SYS_SEM_H) && defined(HAVE_SEMTIMEDOP)
2007-11-12 16:18:49 +01:00
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
namespace Firebird
{
2007-11-12 16:18:49 +01:00
class Semaphore
{
2007-11-12 16:18:49 +01:00
private:
int semId;
union semun
{
2007-11-12 16:18:49 +01:00
int val;
struct semid_ds* buf;
unsigned short* array;
};
void init()
{
semId = semget(IPC_PRIVATE, 1, 0600);
2007-11-12 16:18:49 +01:00
if (semId < 0)
system_call_failed::raise("semaphore.h: Semaphore: semget()");
semun arg;
arg.val = 0;
if (semctl(semId, 0, SETVAL, arg) < 0)
system_call_failed::raise("semaphore.h: Semaphore: semctl()");
}
2008-01-16 07:24:06 +01:00
public:
Semaphore() { init(); }
explicit Semaphore(class MemoryPool&) { init(); }
~Semaphore()
{
2007-11-12 16:18:49 +01:00
semun arg;
if (semctl(semId, 0, IPC_RMID, arg) < 0)
system_call_failed::raise("semaphore.h: ~Semaphore: semctl()");
}
2008-01-16 07:24:06 +01:00
bool tryEnter(int seconds = 0, int milliseconds = 0) // Returns true in case of success
{
milliseconds += seconds * 1000;
2007-11-12 16:18:49 +01:00
timespec timeout;
timeout.tv_sec = time(NULL) + milliseconds / 1000;
timeout.tv_nsec = (milliseconds % 1000) * 1000;
2007-11-12 16:18:49 +01:00
timespec* t = &timeout;
sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = 0;
if (milliseconds < 0) {
2007-11-12 16:18:49 +01:00
// Unlimited wait
t = 0;
}
else if (milliseconds == 0) {
2007-11-12 16:18:49 +01:00
// just try
t = 0;
sb.sem_flg = IPC_NOWAIT;
}
while (semtimedop(semId, &sb, 1, t) < 0)
{
2007-11-17 18:22:41 +01:00
switch (errno)
{
2007-11-12 16:18:49 +01:00
case EAGAIN:
return false;
case EINTR:
continue;
}
system_call_failed::raise("semaphore.h: tryEnter: semop()");
}
2007-11-19 11:30:59 +01:00
return true;
2007-11-12 16:18:49 +01:00
}
2008-01-16 07:24:06 +01:00
void enter()
{
2007-11-12 16:18:49 +01:00
tryEnter(-1);
}
2008-01-16 07:24:06 +01:00
void release(SLONG count = 1)
{
2007-11-12 16:18:49 +01:00
sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = 0;
while (semop(semId, &sb, 1) < 0)
{
2007-11-12 16:18:49 +01:00
if (errno == EINTR) {
continue;
}
system_call_failed::raise("semaphore.h: release: semop()");
}
}
};
} // namespace Firebird
2008-01-16 07:24:06 +01:00
#else // defined(HAVE_SYS_SEM_H) && defined(HAVE_SEMTIMEDOP)
2007-12-07 13:19:37 +01:00
// This implementation will NOT work with FB > 2.1
#ifdef SOLARIS
#error Mutex/Condition based semaphore is NOT OK for Solaris
#endif
#include <pthread.h>
#include <errno.h>
namespace Firebird
{
class Semaphore
{
private:
pthread_mutex_t mu;
pthread_cond_t cv;
void init()
{
int err = pthread_mutex_init(&mu, NULL);
if (err != 0) {
//gds__log("Error on semaphore.h: constructor");
system_call_failed::raise("pthread_mutex_init", err);
}
err = pthread_cond_init(&cv, NULL);
if (err != 0) {
//gds__log("Error on semaphore.h: constructor");
system_call_failed::raise("pthread_cond_init", err);
}
}
public:
Semaphore() { init(); }
explicit Semaphore(class MemoryPool&) { init(); }
~Semaphore()
{
int err = pthread_mutex_destroy(&mu);
if (err != 0) {
//gds__log("Error on semaphore.h: destructor");
//system_call_failed::raise("pthread_mutex_destroy", err);
}
err = pthread_cond_destroy(&cv);
if (err != 0) {
//gds__log("Error on semaphore.h: destructor");
//system_call_failed::raise("pthread_cond_destroy", err);
}
}
bool tryEnter(int seconds = 0, int milliseconds = 0)
{
bool rt = false;
// Return true in case of success
int err2 = 0;
int err = 0;
milliseconds += seconds * 1000;
if (milliseconds == 0)
{
// Instant try
err2 = pthread_mutex_trylock(&mu);
if (err2 == 0)
{
do {
err = pthread_cond_wait(&cv, &mu);
if (err != 0) {
rt = false;
}
else
rt = true;
} while (err == EINTR);
if (err == ETIMEDOUT)
rt = false;
pthread_mutex_unlock(&mu);
return rt;
}
if (err2 == EBUSY)
return false;
system_call_failed::raise("pthread_mutex_trylock", err2);
}
2007-11-17 18:22:41 +01:00
if (milliseconds < 0)
{
// Unlimited wait, like enter()
err2 = pthread_mutex_lock(&mu);
if (err2 == 0)
{
do {
err = pthread_cond_wait(&cv, &mu);
if (err != 0) {
rt = false;
}
else
rt = true;
} while (err == EINTR);
if (err == ETIMEDOUT)
rt = false;
pthread_mutex_unlock(&mu);
return rt;
}
if (err2 == EBUSY)
return false;
system_call_failed::raise("pthread_mutex_lock", err2);
}
2007-11-12 16:18:49 +01:00
// Wait with timeout
timespec timeout;
timeout.tv_sec = time(NULL) + milliseconds / 1000;
timeout.tv_nsec = (milliseconds % 1000) * 1000;
err2 = pthread_mutex_lock(&mu);
2007-11-12 16:18:49 +01:00
if (err2 == 0)
{
do {
err = pthread_cond_timedwait(&cv, &mu, &timeout);
if (err != 0) {
rt = false;
}
else
rt = true;
} while (err == EINTR);
if (err == ETIMEDOUT)
rt = false;
pthread_mutex_unlock(&mu);
return rt;
}
if (err2 == EBUSY)
return false;
system_call_failed::raise("pthread_mutex_lock", err2);
2007-11-12 16:18:49 +01:00
return false; //compiler silencer
}
void enter()
{
int err = 0;
int err2 = pthread_mutex_lock(&mu);
if (err2 == 0)
{
2006-04-17 00:01:41 +02:00
do {
err = pthread_cond_wait(&cv, &mu);
2006-04-17 00:01:41 +02:00
if (err == 0) {
break;
}
} while (err == EINTR);
pthread_mutex_unlock(&mu);
2006-04-17 00:01:41 +02:00
}
else
system_call_failed::raise("pthread_mutex_lock", err2);
}
void release(SLONG count = 1)
{
2006-04-16 21:54:26 +02:00
int err = 0;
for (int i = 0; i < count; i++)
2006-04-16 21:54:26 +02:00
{
err = pthread_mutex_lock(&mu) ;
if (err == 0) {
err = pthread_cond_broadcast(&cv);
if (err != 0) {
system_call_failed::raise("pthread_cond_broadcast", err);
}
pthread_mutex_unlock(&mu);
2006-04-06 10:18:53 +02:00
}
else {
//gds__log("Error on semaphore.h: release");
system_call_failed::raise("pthread_mutex_lock", err);
}
2006-04-16 21:54:26 +02:00
}
}
};
} // namespace Firebird
2008-01-16 07:24:06 +01:00
#endif // defined(HAVE_SYS_SEM_H) && defined(HAVE_SEMTIMEDOP)
2007-11-15 12:31:56 +01:00
2008-01-16 07:24:06 +01:00
#endif // HAVE_SEM_TIMEDWAIT
2007-11-15 12:31:56 +01:00
2008-01-16 07:24:06 +01:00
#endif // WIN_NT
#endif // CLASSES_SEMAPHORE_H