#ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_WARNINGS #include #include #include "../Win32/w32def.h" #endif // _WIN32 #include #include #include #include #ifdef __linux__ #include #include #include #include #include #endif // __linux__ #include #include #include #include "defines.h" #include "mutex.h" ///////////////////////////////////////////////////////////////////////////// #define _IS_VALID_SHMID(id) ((id) >= 0) ///////////////////////////////////////////////////////////////////////////// CGlobalMutex::CGlobalMutex(void) : m_nShmID(_INVALID_ID), m_nSigRefCount(0), m_pMutex(NULL) { memset(&m_mutexAttr, 0, sizeof(m_mutexAttr)); ::pthread_mutexattr_init(&m_mutexAttr); ::pthread_mutexattr_setpshared(&m_mutexAttr, PTHREAD_PROCESS_SHARED); ::pthread_mutexattr_settype(&m_mutexAttr, PTHREAD_MUTEX_RECURSIVE); ::pthread_mutexattr_setrobust(&m_mutexAttr, PTHREAD_MUTEX_ROBUST); } CGlobalMutex::~CGlobalMutex(void) { Release(); } int CGlobalMutex::Create(const uuid_t &ruuid, const char *pszDir) { if(!pszDir) pszDir = ""; size_t nLenReq = strlen(pszDir) + _UUID_STRING_LEN + 10; if(nLenReq > PATH_MAX) { TRACE("CGlobalMutex::Create: directory name too long\n"); errno = EINVAL; return -1; } int nRet = -1; char szUuid[_UUID_STRING_LEN + 1]; char szShm[PATH_MAX]; _uuid_unparse(&ruuid, szUuid, sizeof(szUuid)); sprintf(szShm, "%s/%s.gfa.mtx", pszDir, szUuid); int nFdShm; if((nFdShm = ::open(szShm, O_RDWR | O_CREAT | O_TRUNC, _NUMBER_OF_THE_BEAST)) >= 0) { key_t shmKey; if((shmKey = ::ftok(szShm, 'R')) >= 0) { if((m_nShmID = ::shmget(shmKey, sizeof(pthread_mutex_t), IPC_CREAT | _NUMBER_OF_THE_BEAST)) >= 0) { struct shmid_ds sds; memset(&sds, 0, sizeof(sds)); if(!::shmctl(m_nShmID, IPC_STAT, (struct shmid_ds*)&sds)) { m_pMutex = (pthread_mutex_t*)::shmat(m_nShmID, NULL, 0); if(!sds.shm_atime) { if(!(nRet = ::pthread_mutex_init(m_pMutex, &m_mutexAttr))) { TRACE("CGlobalMutex::Create: Created new Mutex\n"); nRet = 1; } else { TRACE("CGlobalMutex::Create failed! Code: %d\n", nRet); nRet = -1; } } else { TRACE("CGlobalMutex::Create: Opened existing Mutex\n"); nRet = 0; } } else { TRACE("CGlobalMutex::Create: IPC_STAT failed - errno: %d!\n", errno); } } else { TRACE("CGlobalMutex::Create: Failed to create SHM on Key: 0x%08X - errno: %d!\n", shmKey, errno); } } else { TRACE("CGlobalMutex::Create: Failed to create shmKey - errno: %d!\n", errno); } close(nFdShm); } else { TRACE("CGlobalMutex::Create: Failed to open '%s' - errno: %d!\n", szShm, errno); } return nRet; } long CGlobalMutex::Release(void) { struct shmid_ds sds; sds.shm_nattch = (shmatt_t)-1; if(_IS_VALID_SHMID(m_nShmID) && m_pMutex) { if(!::shmctl(m_nShmID, IPC_STAT, (struct shmid_ds*)&sds)) { if((long)sds.shm_nattch > 1) { ::pthread_mutexattr_destroy(&m_mutexAttr); ::shmdt(m_pMutex); ::shmctl(m_nShmID, IPC_STAT, (struct shmid_ds*)&sds); TRACE("CGlobalMutex::Release: Still attached: %ld!\n", (long)(sds.shm_nattch)); } else if((long)sds.shm_nattch == 1) { ::pthread_mutex_destroy(m_pMutex); ::pthread_mutexattr_destroy(&m_mutexAttr); ::shmdt(m_pMutex); ::shmctl(m_nShmID, IPC_RMID, NULL); sds.shm_nattch = 0; TRACE("CGlobalMutex::Release: Mutex removed!\n"); } } else { sds.shm_nattch = (shmatt_t)-1; TRACE("CGlobalMutex::Release: IPC_STAT failed!\n"); } m_pMutex = NULL; m_nShmID = _INVALID_ID; } return (long)sds.shm_nattch; } bool CGlobalMutex::Lock(void) { if(m_pMutex) { int nRet = ::pthread_mutex_lock(m_pMutex); if(nRet == EOWNERDEAD) { TRACE("CGlobalMutex::Lock: Former owner process has died - gained ownership!\n"); ::pthread_mutex_consistent(m_pMutex); nRet = 0; } // TRACE("CGlobalMutex::Lock: Locked: %d.\n", m_nShmID); return !nRet; } else { TRACE("CGlobalMutex::Lock: Invalid Mutex: %p!\n", m_pMutex); errno = EINVAL; return false; } } bool CGlobalMutex::TryLock(void) { if(m_pMutex) { int nRet = ::pthread_mutex_trylock(m_pMutex); if(nRet == EOWNERDEAD) { TRACE("CGlobalMutex::TryLock: Former owner process has died - gained ownership!\n"); ::pthread_mutex_consistent(m_pMutex); nRet = 0; } return !nRet; } else { TRACE("CGlobalMutex::TryLock: Invalid Mutex: %p!\n", m_pMutex); errno = EINVAL; return false; } } bool CGlobalMutex::Unlock(void) { if(m_pMutex) { // TRACE("CGlobalMutex::Unlock: Unlocked: %d.\n", m_nShmID); return !::pthread_mutex_unlock(m_pMutex); } else { TRACE("CGlobalMutex::Unlock: Invalid Mutex: %p!\n", m_pMutex); errno = EINVAL; return false; } } bool CGlobalMutex::LockAndSigBlock(sigset_t *pss) { if(m_pMutex && pss) { if(m_nSigRefCount++ == 0) { ::sigprocmask(SIG_BLOCK, pss, NULL); // TRACE("Block: %lu\n", ::pthread_self()); } return Lock(); } else { TRACE("CGlobalMutex::LockAndSigBlock: Invalid Mutex or sigset: %p!\n", pss); errno = EINVAL; } return false; } bool CGlobalMutex::UnlockAndSigUnblock(sigset_t *pss) { if(m_pMutex && pss) { bool ret = Unlock(); if(--m_nSigRefCount == 0) { ::sigprocmask(SIG_UNBLOCK, pss, NULL); // TRACE("Unblock: %lu\n", ::pthread_self()); } return ret; } else { TRACE("CGlobalMutex::UnlockAndSigBlock: Invalid Mutex or sigset: %p!\n", pss); errno = EINVAL; } return false; }