#ifndef __SYS_PTHREAD__
#define __SYS_PTHREAD__
/******************************************************************************/
/* */
/* X r d S y s P t h r e a d . h h */
/* */
/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
/* Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Department of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#include
#ifdef WIN32
#define HAVE_STRUCT_TIMESPEC 1
#endif
#include
#include
#ifdef AIX
#include
#else
#include
#endif
#ifdef __APPLE__
#ifndef CLOCK_REALTIME
#include
#include
#endif
namespace
{
template< typename TYPE >
void get_apple_realtime( TYPE & wait )
{
#ifdef CLOCK_REALTIME
clock_gettime(CLOCK_REALTIME, &wait);
#else
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
wait.tv_sec = mts.tv_sec;
wait.tv_nsec = mts.tv_nsec;
#endif
}
}
#endif
#include "XrdSys/XrdSysError.hh"
/******************************************************************************/
/* X r d S y s C o n d V a r */
/******************************************************************************/
// XrdSysCondVar implements the standard POSIX-compliant condition variable.
// Methods correspond to the equivalent pthread condvar functions.
class XrdSysCondVar
{
public:
inline void Lock() {pthread_mutex_lock(&cmut);}
inline void Signal() {if (relMutex) pthread_mutex_lock(&cmut);
pthread_cond_signal(&cvar);
if (relMutex) pthread_mutex_unlock(&cmut);
}
inline void Broadcast() {if (relMutex) pthread_mutex_lock(&cmut);
pthread_cond_broadcast(&cvar);
if (relMutex) pthread_mutex_unlock(&cmut);
}
inline void UnLock() {pthread_mutex_unlock(&cmut);}
int Wait();
int Wait(int sec);
int WaitMS(int msec);
XrdSysCondVar( int relm=1, // 0->Caller will handle lock/unlock
const char *cid=0 // ID string for debugging only
) {pthread_cond_init(&cvar, NULL);
pthread_mutex_init(&cmut, NULL);
relMutex = relm; condID = (cid ? cid : "unk");
}
~XrdSysCondVar() {pthread_cond_destroy(&cvar);
pthread_mutex_destroy(&cmut);
}
private:
pthread_cond_t cvar;
pthread_mutex_t cmut;
int relMutex;
const char *condID;
};
/******************************************************************************/
/* X r d S y s C o n d V a r H e l p e r */
/******************************************************************************/
// XrdSysCondVarHelper is used to implement monitors with the Lock of a a condvar.
// Monitors are used to lock
// whole regions of code (e.g., a method) and automatically
// unlock with exiting the region (e.g., return). The
// methods should be self-evident.
class XrdSysCondVarHelper
{
public:
inline void Lock(XrdSysCondVar *CndVar)
{if (cnd) {if (cnd != CndVar) cnd->UnLock();
else return;
}
CndVar->Lock();
cnd = CndVar;
};
inline void UnLock() {if (cnd) {cnd->UnLock(); cnd = 0;}}
XrdSysCondVarHelper(XrdSysCondVar *CndVar=0)
{if (CndVar) CndVar->Lock();
cnd = CndVar;
}
XrdSysCondVarHelper(XrdSysCondVar &CndVar)
{CndVar.Lock();
cnd = &CndVar;
}
~XrdSysCondVarHelper() {if (cnd) UnLock();}
private:
XrdSysCondVar *cnd;
};
/******************************************************************************/
/* X r d S y s M u t e x */
/******************************************************************************/
// XrdSysMutex implements the standard POSIX mutex. The methods correspond
// to the equivalent pthread mutex functions.
class XrdSysMutex
{
public:
friend class XrdSysCondVar2;
inline int CondLock()
{if (pthread_mutex_trylock( &cs )) return 0;
return 1;
}
#ifdef __APPLE__
inline int TimedLock( int wait_ms )
{
struct timespec wait, cur, dur;
get_apple_realtime(wait);
wait.tv_sec += (wait_ms / 1000);
wait.tv_nsec += (wait_ms % 1000) * 1000000;
wait.tv_sec += (wait.tv_nsec / 1000000000);
wait.tv_nsec = wait.tv_nsec % 1000000000;
int rc;
while( ( rc = pthread_mutex_trylock( &cs ) ) == EBUSY )
{
get_apple_realtime(cur);
if( ( cur.tv_sec > wait.tv_sec ) ||
( ( cur.tv_sec == wait.tv_sec ) && ( cur.tv_nsec >= wait.tv_nsec ) ) )
return 0;
dur.tv_sec = wait.tv_sec - cur.tv_sec;
dur.tv_nsec = wait.tv_nsec - cur.tv_nsec;
if( dur.tv_nsec < 0 )
{
--dur.tv_sec;
dur.tv_nsec += 1000000000;
}
if( ( dur.tv_sec != 0 ) || ( dur.tv_nsec > 1000000 ) )
{
dur.tv_sec = 0;
dur.tv_nsec = 1000000;
}
nanosleep( &dur, 0 );
}
return !rc;
}
#else
inline int TimedLock(int wait_ms)
{struct timespec wait;
clock_gettime(CLOCK_REALTIME, &wait);
wait.tv_sec += (wait_ms / 1000);
wait.tv_nsec += (wait_ms % 1000) * 1000000;
wait.tv_sec += (wait.tv_nsec / 1000000000);
wait.tv_nsec = wait.tv_nsec % 1000000000;
return !pthread_mutex_timedlock(&cs, &wait);
}
#endif
inline void Lock() {pthread_mutex_lock(&cs);}
inline void UnLock() {pthread_mutex_unlock(&cs);}
XrdSysMutex() {pthread_mutex_init(&cs, NULL);}
~XrdSysMutex() {pthread_mutex_destroy(&cs);}
protected:
pthread_mutex_t cs;
};
/******************************************************************************/
/* X r d S y s R e c M u t e x */
/******************************************************************************/
// XrdSysRecMutex implements the recursive POSIX mutex. The methods correspond
// to the equivalent pthread mutex functions.
class XrdSysRecMutex: public XrdSysMutex
{
public:
XrdSysRecMutex();
int InitRecMutex();
int ReInitRecMutex();
};
/******************************************************************************/
/* X r d S y s M u t e x H e l p e r */
/******************************************************************************/
// XrdSysMutexHelper us ised to implement monitors. Monitors are used to lock
// whole regions of code (e.g., a method) and automatically
// unlock with exiting the region (e.g., return). The
// methods should be self-evident.
class XrdSysMutexHelper
{
public:
inline void Lock(XrdSysMutex *Mutex)
{if (mtx) {if (mtx != Mutex) mtx->UnLock();
else return;
}
Mutex->Lock();
mtx = Mutex;
};
inline void UnLock() {if (mtx) {mtx->UnLock(); mtx = 0;}}
XrdSysMutexHelper(XrdSysMutex *mutex=0)
{if (mutex) mutex->Lock();
mtx = mutex;
}
XrdSysMutexHelper(XrdSysMutex &mutex)
{mutex.Lock();
mtx = &mutex;
}
~XrdSysMutexHelper() {if (mtx) UnLock();}
private:
XrdSysMutex *mtx;
};
/******************************************************************************/
/* X r d S y s C o n d V a r 2 */
/******************************************************************************/
// XrdSysCondVar2 implements the standard POSIX-compliant condition variable but
// unlike XrdSysCondVar requires the caller to supply a working
// mutex and does not handle any locking other than what is
// defined by POSIX.
class XrdSysCondVar2
{
public:
inline void Signal() {pthread_cond_signal(&cvar);}
inline void Broadcast() {pthread_cond_broadcast(&cvar);}
inline int Wait() {return pthread_cond_wait(&cvar, mtxP);}
bool Wait(int sec) {return WaitMS(sec*1000);}
bool WaitMS(int msec);
XrdSysCondVar2(XrdSysMutex &mtx) : mtxP(&mtx.cs)
{pthread_cond_init(&cvar, NULL);}
~XrdSysCondVar2() {pthread_cond_destroy(&cvar);}
protected:
pthread_cond_t cvar;
pthread_mutex_t *mtxP;
};
/******************************************************************************/
/* X r d S y s R W L o c k */
/******************************************************************************/
// XrdSysRWLock implements the standard POSIX wrlock mutex. The methods correspond
// to the equivalent pthread wrlock functions.
class XrdSysRWLock
{
public:
inline int CondReadLock()
{if (pthread_rwlock_tryrdlock( &lock )) return 0;
return 1;
}
inline int CondWriteLock()
{if (pthread_rwlock_trywrlock( &lock )) return 0;
return 1;
}
inline void ReadLock() {pthread_rwlock_rdlock(&lock);}
inline void WriteLock() {pthread_rwlock_wrlock(&lock);}
inline void ReadLock( int &status ) {status = pthread_rwlock_rdlock(&lock);}
inline void WriteLock( int &status ) {status = pthread_rwlock_wrlock(&lock);}
inline void UnLock() {pthread_rwlock_unlock(&lock);}
enum PrefType {prefWR=1};
XrdSysRWLock(PrefType /* ptype */)
{
#if defined(__linux__) && (defined(__GLIBC__) || defined(__UCLIBC__))
pthread_rwlockattr_t attr;
pthread_rwlockattr_setkind_np(&attr,
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
pthread_rwlock_init(&lock, &attr);
#else
pthread_rwlock_init(&lock, NULL);
#endif
}
XrdSysRWLock() {pthread_rwlock_init(&lock, NULL);}
~XrdSysRWLock() {pthread_rwlock_destroy(&lock);}
inline void ReInitialize(PrefType /* ptype */)
{
pthread_rwlock_destroy(&lock);
#if defined(__linux__) && (defined(__GLIBC__) || defined(__UCLIBC__))
pthread_rwlockattr_t attr;
pthread_rwlockattr_setkind_np(&attr,
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
pthread_rwlock_init(&lock, &attr);
#else
pthread_rwlock_init(&lock, NULL);
#endif
}
inline void ReInitialize()
{
pthread_rwlock_destroy(&lock);
pthread_rwlock_init(&lock, NULL);
}
protected:
pthread_rwlock_t lock;
};
/******************************************************************************/
/* X r d S y s W R L o c k H e l p e r */
/******************************************************************************/
// XrdSysWRLockHelper : helper class for XrdSysRWLock
class XrdSysRWLockHelper
{
public:
inline void Lock(XrdSysRWLock *lock, bool rd = 1)
{if (lck) {if (lck != lock) lck->UnLock();
else return;
}
if (rd) lock->ReadLock();
else lock->WriteLock();
lck = lock;
};
inline void UnLock() {if (lck) {lck->UnLock(); lck = 0;}}
XrdSysRWLockHelper(XrdSysRWLock *l=0, bool rd = 1)
{ if (l) {if (rd) l->ReadLock();
else l->WriteLock();
}
lck = l;
}
XrdSysRWLockHelper(XrdSysRWLock &l, bool rd = 1)
{ if (rd) l.ReadLock();
else l.WriteLock();
lck = &l;
}
~XrdSysRWLockHelper() {if (lck) UnLock();}
private:
XrdSysRWLock *lck;
};
/******************************************************************************/
/* X r d S y s F u s e d M u t e x */
/******************************************************************************/
class XrdSysFusedMutex
{
public:
inline void Lock() {isRW ? rwLok->WriteLock() : mutex->Lock();}
inline void ReadLock() {isRW ? rwLok->ReadLock() : mutex->Lock();}
inline void WriteLock() {isRW ? rwLok->WriteLock() : mutex->Lock();}
inline void UnLock() {isRW ? rwLok->UnLock() : mutex->UnLock();}
XrdSysFusedMutex(XrdSysRWLock &mtx)
: rwLok(&mtx), isRW(true) {}
XrdSysFusedMutex(XrdSysMutex &mtx)
: mutex(&mtx), isRW(false) {}
~XrdSysFusedMutex() {}
private:
union {XrdSysRWLock *rwLok; XrdSysMutex *mutex;};
bool isRW;
};
/******************************************************************************/
/* X r d S y s S e m a p h o r e */
/******************************************************************************/
// XrdSysSemaphore implements the classic counting semaphore. The methods
// should be self-evident. Note that on certain platforms
// semaphores need to be implemented based on condition
// variables since no native implementation is available.
#if defined(__APPLE__) || defined(__GNU__)
class XrdSysSemaphore
{
public:
int CondWait();
void Post();
void Wait();
static void CleanUp(void *semVar);
XrdSysSemaphore(int semval=1,const char *cid=0) : semVar(0, cid)
{semVal = semval; semWait = 0;}
~XrdSysSemaphore() {}
private:
XrdSysCondVar semVar;
int semVal;
int semWait;
};
#else
class XrdSysSemaphore
{
public:
inline int CondWait()
{while(sem_trywait( &h_semaphore ))
{if (errno == EAGAIN) return 0;
if (errno != EINTR) { throw "sem_CondWait() failed";}
}
return 1;
}
inline void Post() {if (sem_post(&h_semaphore))
{throw "sem_post() failed";}
}
inline void Wait() {while (sem_wait(&h_semaphore))
{if (EINTR != errno)
{throw "sem_wait() failed";}
}
}
XrdSysSemaphore(int semval=1, const char * =0)
{if (sem_init(&h_semaphore, 0, semval))
{throw "sem_init() failed";}
}
~XrdSysSemaphore() {if (sem_destroy(&h_semaphore))
{abort();}
}
private:
sem_t h_semaphore;
};
#endif
/******************************************************************************/
/* X r d S y s T h r e a d */
/******************************************************************************/
// The C++ standard makes it impossible to link extern "C" methods with C++
// methods. Thus, making a full thread object is nearly impossible. So, this
// object is used as the thread manager. Since it is static for all intense
// and purposes, one does not need to create an instance of it.
//
// Options to Run()
//
// BIND creates threads that are bound to a kernel thread.
//
#define XRDSYSTHREAD_BIND 0x001
// HOLD creates a thread that needs to be joined to get its ending value.
// Otherwise, a detached thread is created.
//
#define XRDSYSTHREAD_HOLD 0x002
class XrdSysThread
{
public:
static int Cancel(pthread_t tid) {return pthread_cancel(tid);}
static int Detach(pthread_t tid) {return pthread_detach(tid);}
static int SetCancelOff() {
return pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
};
static int Join(pthread_t tid, void **ret) {
return pthread_join(tid, ret);
};
static int SetCancelOn() {
return pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
};
static int SetCancelAsynchronous() {
return pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
};
static int SetCancelDeferred() {
return pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0);
};
static void CancelPoint() {
pthread_testcancel();
};
static pthread_t ID(void) {return pthread_self();}
static int Kill(pthread_t tid) {return pthread_cancel(tid);}
static unsigned long Num(void);
static int Run(pthread_t *, void *(*proc)(void *), void *arg,
int opts=0, const char *desc = 0);
static int Same(pthread_t t1, pthread_t t2)
{return pthread_equal(t1, t2);}
static void setDebug(XrdSysError *erp) {eDest = erp;}
static void setStackSize(size_t stsz, bool force=false);
static int Signal(pthread_t tid, int snum)
{return pthread_kill(tid, snum);}
static int Wait(pthread_t tid);
XrdSysThread() {}
~XrdSysThread() {}
private:
static XrdSysError *eDest;
static size_t stackSize;
};
#endif