// // File: hzLock.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library 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 any later version. // // The HadronZoo C++ Class Library 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 the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
// // Desc: General functions to provide singleton processes, demons etc //
#include <iostream> #include <fstream>
#include <unistd.h> #include <fcntl.h> #include <dirent.h> #include <pthread.h> #include <execinfo.h> #include <signal.h> #include <sys/ipc.h> #include <sys/sem.h>
#include <sys/stat.h>
#include "hzTextproc.h" #include "hzLock.h" #include "hzDirectory.h"
/* ** Variables */
static hzLockS s_crMtx ; // Mutex to control creation of mutexes! static hzLockRWD* s_allMtx = 0 ; // First link in linked list of all active mutext (with it's own mutex deactivated) static hzLockRWD* s_lastMtx = 0 ; // Last link in linked list of all active mutext (with it's own mutex deactivated) static uint32_t s_SeqMutex = 100 ; // For unique ids of hzLockRWD instances
/* ** SECTION 1: hzLockS. Simple Lock */
hzEcode hzLockS::Lock (int32_t nTries) { // Obtain a lock on a resource. This will spin until either the lock is granted or the lock is deactivated (host entity destructed) // // Arguments: 1) nTries Number of retries (in thousands) before abandoning efforts to obtain the lock // // Returns: E_NOTFOUND If the lock has been killed by a previous holder // E_TIMEOUT If the lock has not been release within nTries // E_OK If the lock is granted // // Note: This will return E_OK immeadiately if _hzGlobal_MT is false (the program is single threaded)
uint32_t cont ; // Contention or spin count uint32_t tries ; // Contention or spin count uint32_t tid ; // Thread id uint32_t limit ; // Timeout as number of tries
if (!_hzGlobal_MT) return E_OK ;
tid = pthread_self() ; limit = nTries < 0 ? 0xfffffffe : nTries * 1000 ;
if (m_lockval == tid) Fatal("hzLockS::hzLockS. Attempt by thread %u to re-lock address %p\n", tid, &m_lockval) ;
for (tries = cont = 0 ;;) { if (m_lockval == 0xffffffff) return E_NOTFOUND ;
if (m_lockval) { cont++ ; continue ; }
if (!__sync_val_compare_and_swap(&m_lockval, 0, tid)) { if (m_lockval == tid) break ; }
tries++ ; if (tries > limit) return E_TIMEOUT ; }
m_lockval = tid ; return E_OK ; }
void hzLockS::Kill (void) { // Releases a lock on a resource but instead of leaving the lock free (a value of 0), it sets the lock to invalid (a value of 0xffffffff). This signals to // any thread waiting on the lock, that it must abandon the intended operation on the resource protected by the lock. Kill is called where the resourse is // about to be deleted. // // Arguments: None // Returns: None // // Note this operation terminates execution if the current thread is trying to unlock a resource that is locked by another thread or not locked at all
uint32_t tid ; // Caller thread id
if (_hzGlobal_MT) { tid = pthread_self() ;
if (!m_lockval) Fatal("hzLockS::hzKill. Attempt by thread %u to kill unaquired lock\n", tid) ;
if (m_lockval == 0xffffffff) Fatal("hzLockS::hzKill. Attempt by thread %u to kill a deprecated lock\n", tid) ;
if (m_lockval != tid) Fatal("hzLockS::hzKill. Attempt by thread %u to kill lock aquired by thread (%u)\n", tid, m_lockval) ;
m_lockval = 0xffffffff ; } }
void hzLockS::Unlock (void) { // Release a lock on a resource // // Arguments: None // Returns: None // // Note this operation terminates execution if the current thread is trying to unlock a resource that is locked by another thread or not locked at all
uint32_t tid ; // Caller thread id
if (_hzGlobal_MT) { tid = pthread_self() ;
if (!m_lockval) Fatal("Attempt by thread %u to unlock address %p that is not locked by any thread", tid, &m_lockval) ;
if (m_lockval == 0xffffffff) Fatal("hzLockS::hzKill. Attempt by thread %u to unlock a deprected lock\n", tid) ;
if (m_lockval != tid) Fatal("Attempt by thread %u to unlock address %p that is locked by another thread (%u)", tid, &m_lockval, m_lockval) ;
m_lockval = 0 ; } }
/* ** SECTION 2: hzLockRW. Read/Write lock */
hzEcode hzLockRW::LockWrite (int32_t nTries) { // Obtain a write lock on a resource. This will spin until either the lock is granted or the lock is deactivated (host entity destructed). And // then spin until the read count falls to zero. This latter step ensures no thread can read the resource during a write. // // Arguments: 1) nTries Number of retries (in thousands) before abandoning efforts to obtain the lock // // Returns: E_NOTFOUND The previous thread with write access killed the lock (should be because it deleted the applicable resource) // E_TIMEOUT The thread holding write access has held the lock for too long // E_OK Write access granted
_hzfunc("hzLockRW::LockWrite") ;
uint32_t cont ; // Contention or spin count uint32_t tries ; // Contention or spin count uint32_t tid ; // Thread id uint32_t limit ; // Timeout as number of tries
if (!_hzGlobal_MT) return E_OK ;
tid = pthread_self() ; limit = nTries < 0 ? 0xfffffffe : nTries * 1000 ;
if (m_lockval == tid) Fatal("Attempt by thread %u to re-lock address %p\n", tid, &m_lockval) ;
// Wait for lock to become free (no other threads with write access) for (tries = cont = 0 ;;) { if (m_lockval == 0xffffffff) return E_NOTFOUND ;
if (m_lockval) { cont++ ; continue ; }
// Try to grab the lock if (!__sync_val_compare_and_swap(&m_lockval, 0, tid)) { if (m_lockval == tid) break ; }
// Check for timeout tries++ ; if (tries > limit) return E_TIMEOUT ; }
// Wait for counter to drop to zero for (; m_counter ; cont++) ; return E_OK ; }
hzEcode hzLockRW::LockRead (int32_t timeout) { // Obtain a read lock on a resource. This is only spin if there is a write lock in place. // // Arguments: 1) timout The timeout limit (limit of retries in thousands) // // Returns: E_NOTFOUND The previous thread with write access killed the lock (should be because it deleted the applicable resource) // E_TIMEOUT The thread holding write access has held the lock for too long // E_OK Write access granted
_hzfunc("hzLockRW::LockRead") ;
uint32_t cont ; // Contention or spin count uint32_t tid ; // Caller thread id
if (!_hzGlobal_MT) return E_OK ;
tid = pthread_self() ;
if (m_lockval == 0xffffffff) return E_NOTFOUND ;
if (m_lockval == tid) Fatal("Attempt by thread %u to re-lock address %p\n", tid, &m_lockval) ;
// Spin until the lock is free (m_lockval == 0) for (cont = 0 ; m_lockval ; cont++) { if (m_lockval == 0xffffffff) return E_NOTFOUND ; if (!m_lockval) break ; }
// Increment count of read locks obtained while (__sync_add_and_fetch(&m_counter, 1)) ; return E_OK ; }
void hzLockRW::Kill (void) { // Kill a lock (signal that the resouce controlled by the lock is to be deleted). The calling thread must hold the write lock. This function will terminate // the program if this is not the case or if the lock has already been killed. // // Arguments: None // Returns: None
uint32_t tid ; // Caller thread id
if (_hzGlobal_MT) { tid = pthread_self() ;
if (!m_lockval) Fatal("hzLockRW::hzKill. Attempt by thread %u to kill unaquired lock\n", tid) ;
if (m_lockval == 0xffffffff) Fatal("hzLockRW::hzKill. Attempt by thread %u to kill a deprecated lock\n", tid) ;
if (m_lockval != tid) Fatal("hzLockRW::hzKill. Attempt by thread %u to kill lock aquired by thread (%u)\n", tid, m_lockval) ;
m_lockval = 0xffffffff ; } }
void hzLockRW::Unlock (void) { // Release a lock on a resource. This could be either a write lock where the lock value equals the thread id of the calling thread, or a read lock where it // does not. If the lock value equals this thread id, this thread has the write lock and the action is simply to release it and return. There is no need to // use a sync function to do this as no other thread can have write access and no need to check the counter as no other thread can obtain read access while // any thread has the write lock. // // If the lock value does not equal this thread id this thread only has a read lock so the action is only to decrement the lock using sync. The counter is // checked beforehand to see if it is already zero. The program is terminated if the lock is already zero. // // Arguments: None // Returns: None
_hzfunc("hzLockRWD::Unlock") ;
uint32_t tid ; // Caller thread id
if (_hzGlobal_MT) { if (m_lockval == 0xffffffff) hzexit(E_CORRUPT, "Attempting to unlock a deprecated lock") ;
tid = pthread_self() ; if (m_lockval == tid) { // This thread has the write lock so just release it m_lockval = 0 ; } else { // Assume a read lock decrement counter but do nothing if the lock has been killed if (m_lockval != 0xffffffff) { if (m_counter == 0) hzexit(E_CORRUPT, "Lock count already zero") ; while (__sync_add_and_fetch(&m_counter, -1)) ; } } } }
/* ** SECTION 3: hzLockRWD. Read-Write Lock with Diagnostics */
hzLockRWD::hzLockRWD (void) { // hzLockRWD default constructor. Note that this assigns the mutex internal structure using malloc in order to avoid using new which is overloaded and // itself controlled by a mutex etc! // // Arguments: None
next = prev = 0 ; m_Granted = m_WaitThis = m_WaitTotal = m_Inuse = m_SpinsTotal = 0 ; m_SpinsThis = m_TriesTotal = m_TriesThis = m_LockOpsW = m_LockOpsR = m_Unlocks = 0 ; m_lockval = m_counter = 0 ; m_Id = m_recurse = 0 ; m_name[0] = 0 ;
s_crMtx.Lock() ;
m_Id = ++s_SeqMutex ;
if (!s_allMtx) s_allMtx = s_lastMtx = this ; else { prev = s_lastMtx ; s_lastMtx->next = this ; s_lastMtx = this ; }
s_crMtx.Unlock() ; }
hzLockRWD::hzLockRWD (const char* name) { // hzLockRWD default constructor but with name supplied for diagnostic purposes. // // Arguments: 1) name Lock name
next = prev = 0 ; m_Granted = m_WaitThis = m_WaitTotal = m_Inuse = m_SpinsTotal = 0 ; m_SpinsThis = m_TriesTotal = m_TriesThis = m_LockOpsW = m_LockOpsR = m_Unlocks = 0 ; m_lockval = m_counter = 0 ; m_Id = m_recurse = 0 ; m_name[0] = 0 ;
s_crMtx.Lock() ;
m_Id = ++s_SeqMutex ; strncpy(m_name, name, 15) ;
if (!s_allMtx) s_allMtx = s_lastMtx = this ; else { prev = s_lastMtx ; s_lastMtx->next = this ; s_lastMtx = this ; }
s_crMtx.Unlock() ; }
hzLockRWD::~hzLockRWD (void) { // hzLockRWD destructor. // // Arguments: None
s_crMtx.Lock() ;
if (s_allMtx == this) s_allMtx = s_allMtx->next ; if (this == s_lastMtx) s_lastMtx = s_lastMtx->prev ;
if (prev) prev->next = next ; if (next) next->prev = prev ;
s_crMtx.Unlock() ; }
hzEcode hzLockRWD::Setname (const char* name) { // Set diagnostic name on lock as an explicit step. This can only be done once. Subsequent calls will have no effect // // Arguments: 1) name Lock name // // Returns: E_SETONCE If the name already set // E_OK If the operaton was successful
if (m_name[0]) return E_SETONCE ; strncpy(m_name, name, 15) ; return E_OK ; }
void* hzLockRWD::Address (void) { return this ; } uint64_t hzLockRWD::Granted (void) { return m_Granted ; } uint64_t hzLockRWD::WaitThis (void) { return m_WaitThis ; } uint64_t hzLockRWD::WaitTotal (void) { return m_WaitTotal ; } uint64_t hzLockRWD::Inuse (void) { return m_Inuse ; } uint64_t hzLockRWD::SpinTotal (void) { return m_SpinsTotal ; } uint32_t hzLockRWD::SpinThis (void) { return m_SpinsThis ; } uint32_t hzLockRWD::TriesTotal (void) { return m_TriesTotal ; } uint32_t hzLockRWD::TriesThis (void) { return m_TriesThis ; } uint32_t hzLockRWD::Thread (void) { return m_lockval ; } uint32_t hzLockRWD::Level (void) { return m_recurse ; } uint32_t hzLockRWD::UID (void) { return m_Id ; } const char* hzLockRWD::Name (void) { return m_name ; }
hzEcode hzLockRWD::LockWrite (int32_t timeout) { // Obtain a write lock on a resource. This will spin until either the lock is granted or the lock is deactivated (host entity destructed). And // then spin until the read count falls to zero. This latter step ensures no thread can read the resource during a write. // // Arguments: 1) timout The limit of retries (in thousands) // // Returns: E_NOTFOUND The previous thread with write access killed the lock (should be because it deleted the applicable resource) // E_TIMEOUT The thread holding write access has held the lock for too long // E_OK Write access granted
_hzfunc("hzLockRWD::LockWrite") ;
uint64_t now ; // Time lock sought uint64_t got ; // Time lock granted uint32_t cont ; // Contention or spin count uint32_t tries ; // Contention or spin count uint32_t tid ; // Thread id uint32_t limit ; // Timeout as number of tries
if (!_hzGlobal_MT) return E_OK ;
now = RealtimeNano() ; tid = pthread_self() ; limit = timeout < 0 ? 0xfffffffe : timeout * 1000 ;
if (m_lockval == tid) { m_recurse++ ; //m_SpinLocksR++ ; m_LockOpsW++ ; m_SpinsThis = 0 ; return E_OK ; }
for (tries = cont = 0 ;;) { if (m_lockval) { cont++ ; continue ; }
if (!__sync_val_compare_and_swap(&(m_lockval), 0, tid)) { if (m_lockval == tid) break ; }
tries++ ; if (tries > limit) return E_TIMEOUT ; }
// Wait for counter to drop to zero for (; m_counter ; cont++) ;
// Note: Can only set m_lockval when we have the lock m_lockval = tid ; m_recurse = 0 ; //m_SpinLocksP++ ; m_LockOpsW++ ; m_TriesThis = tries ; m_TriesTotal += tries ; m_SpinsThis = cont ; m_SpinsTotal += cont ;
got = RealtimeNano() ;
m_Granted = got ; m_WaitThis = (got - now) ; m_WaitTotal += m_WaitThis ; return E_OK ; }
hzEcode hzLockRWD::LockRead (int32_t nTries) { // Obtain a read lock on a resource. This is only spin if there is a write lock in place. // // Arguments: 1) timout The timeout limit (limit of retries in thousands) // // Returns: E_NOTFOUND The previous thread with write access killed the lock (should be because it deleted the applicable resource) // E_TIMEOUT The thread holding write access has held the lock for too long // E_OK Write access granted
_hzfunc("hzLockRWD::LockRead") ;
uint32_t cont ; // Contention or spin count uint32_t tid ; // Thread id
if (!_hzGlobal_MT) return E_OK ;
tid = pthread_self() ;
if (m_lockval == 0xffffffff) return E_NOTFOUND ;
if (m_lockval == tid) Fatal("Attempt by thread %u to re-lock address %p\n", tid, &m_lockval) ;
for (cont = 0 ; m_lockval ; cont++) { if (m_lockval == 0xffffffff) return E_NOTFOUND ; if (!m_lockval) break ; }
while (__sync_add_and_fetch(&m_counter, 1)) ; return E_OK ; }
void hzLockRWD::Kill (void) { // Kill a lock (signal that the resouce controlled by the lock is to be deleted). The calling thread must hold the write lock. This function will terminate // the program if this is not the case or if the lock has already been killed. // // Arguments: None // Returns: None
uint32_t tid ; // Caller thread id
if (_hzGlobal_MT) { tid = pthread_self() ;
if (!m_lockval) Fatal("hzLockRW::hzKill. Attempt by thread %u to kill unaquired lock\n", tid) ;
if (m_lockval == 0xffffffff) Fatal("hzLockRW::hzKill. Attempt by thread %u to kill a deprecated lock\n", tid) ;
if (m_lockval != tid) Fatal("hzLockRW::hzKill. Attempt by thread %u to kill lock aquired by thread (%u)\n", tid, m_lockval) ;
m_lockval = 0xffffffff ; } }
void hzLockRWD::Unlock (void) { // Release a lock on a resource // // Arguments: None // Returns: None // // Note this operation terminates execution if the current thread is trying to unlock a resource that is locked by another thread or not locked at all
_hzfunc("hzLockRWD::Unlock") ;
uint32_t tid ; // Caller thread id
/* if (_hzGlobal_MT) { tid = pthread_self() ;
if (!m_lockval) hzexit(E_CORRUPT, "Attempting to unlock mutex %d that is not locked by any thread", m_Id) ;
if (m_lockval != tid) hzexit(E_CORRUPT, "Attempting to unlock mutex %d that is locked by another thread (%u)", m_Id, m_lockval) ;
// Must unset m_lockval before we release the lock if (m_recurse) { m_recurse-- ; m_UnlocksR++ ; } else { m_lockval = 0 ; m_UnlocksP++ ; m_Inuse += (RealtimeNano() - m_Granted) ; } } */
// Program multithreaded? if (_hzGlobal_MT) { if (m_lockval == 0xffffffff) hzexit(E_CORRUPT, "Attempting to unlock a deprecated lock") ;
tid = pthread_self() ; if (m_lockval == tid) { // This thread has the write lock so just release it m_lockval = 0 ; } else { // Assume a read lock decrement counter but do nothing if the lock has been killed if (m_lockval != 0xffffffff) { if (m_counter == 0) hzexit(E_CORRUPT, "Lock count already zero") ; while (__sync_add_and_fetch(&m_counter, -1)) ; } } } }
void ReportMutexContention (hzChain& Z, bool bHtml) { // Category: Diagnostics // // Provide a snapshot report on all active mutexes. This will be the total time the mutex was locked and the total time spent by threads waiting // for the lock to become available. // // Please note that the values are first copied from the mutex internals and then converted to strings and used to agregate to the output. This // extra step is needed because the string manipulations affect the mutex that controls string memory allocation! This does not invalidate the // report though as it is intended as a snapshot of mutexs at the point it is called. // // Arguments: 1) Z Reference to chain populated by the report // 2) bHtml True if the report is to be fashioned as a web page // // Returns: None
_hzfunc(__func__) ;
static hzString S = "Untitled" ;
hzXDate now ; // Time now hzLockRWD* pMtx ; // The lock double fWait ; // Total nanoseconds wait time double fInuse ; // Total nanoseconds in-use double ratio ; // Ratio of in-use time to wait + inuse time hzString vals[9] ; // Lock activity report fields uint64_t nWaitTotal ; // Total nanoseconds wait time uint64_t nInuse ; // Total nanoseconds in-use uint64_t nSpinsTotal ; // Total spins uint32_t nTriesTotal ; // Total compare and swaps uint32_t nLockOpsW ; // Total calls to LockWrite uint32_t nLockOpsR ; // Total calls to LockRead uint32_t nUnlocks ; // Total Unlocks char buf[24] ; // Mutex name buffer
now.SysDateTime() ; if (bHtml) { Z.Printf("<p><center>Mutex Contention Report at %s</center></p>\n", *now) ;
Z << "<div align=\"right\">\n" "<table width=\"90%\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" " "style=\"text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; color:#000000;\">\n" "\t<tr>\n" "\t\t<th>Mutex ID</th>\n" "\t\t<th>Wr Locks</th>\n" "\t\t<th>Rd Locks</th>\n" "\t\t<th>Unlocks</th>\n" "\t\t<th>Status</th>\n" "\t\t<th>Spins</th>\n" "\t\t<th>Tries</th>\n" "\t\t<th>Wait/Overhead</th>\n" "\t\t<th>Lock Duration</th>\n" "\t\t<th>Ratio</th>\n" "\t\t<th>Name</th>\n" "\t</tr>\n" ; } else { Z.Printf("Mutex Contention Report at %s\n", *now) ; Z.Printf("Mutex ID Wr Locks Rd Locks Unlocks Status Spins Tries Wait/Overhead Lock Duration Ratio Name\n") ; }
for (pMtx = s_allMtx ; pMtx ; pMtx = pMtx->next) { fWait = pMtx->m_WaitTotal ; fInuse = pMtx->m_Inuse ; if (fInuse) ratio = fInuse/(fWait + fInuse) ; else ratio = 0.0 ; sprintf(buf, "%f", ratio) ;
nLockOpsW = pMtx->m_LockOpsW ; nLockOpsR = pMtx->m_LockOpsR ; nUnlocks = pMtx->m_Unlocks ; nSpinsTotal = pMtx->m_SpinsTotal ; nTriesTotal = pMtx->m_TriesTotal ; nWaitTotal = pMtx->m_WaitTotal ; nInuse = pMtx->m_Inuse ;
vals[0] = FormalNumber(nLockOpsW, 12) ; vals[1] = FormalNumber(nLockOpsR, 12) ; vals[2] = FormalNumber(nUnlocks, 12) ; vals[3] = FormalNumber(nSpinsTotal, 15) ; vals[4] = FormalNumber(nTriesTotal, 15) ; vals[5] = FormalNumber(nWaitTotal, 15) ; vals[6] = FormalNumber(nInuse, 15) ;
if (bHtml) { Z << "\t<tr align=\"right\">\n" ; Z.Printf("<td>%03d</td>", pMtx->m_Id) ; Z.Printf("<td>%s</td>", *vals[0]) ; Z.Printf("<td>%s</td>", *vals[1]) ; Z.Printf("<td>%s</td>", *vals[2]) ; Z.Printf("<td>%11u</td>", pMtx->m_lockval) ; Z.Printf("<td>%s</td>", *vals[3]) ; Z.Printf("<td>%s</td>", *vals[4]) ; Z.Printf("<td>%s</td>", *vals[5]) ; Z.Printf("<td>%s</td>", *vals[6]) ; Z.Printf("<td>%s</td>", buf) ; Z.Printf("<td align=\"left\">%s</td>", *S) ; Z << "\t</tr>\n" ; } else { Z.Printf("%03d %s %s %s %llu %s %s %s %s %s %s\n", pMtx->m_Id, *vals[0], *vals[1], *vals[2], pMtx->m_lockval, *vals[3], *vals[4], *vals[5], *vals[6], buf, *S) ; } }
if (bHtml) Z << "</table>\n" ; }
/* ** SECTION 4: Semaphore based locking */
#define HZLOCKNOKEY S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH|IPC_CREAT // Semaphore settings for 'lock no key' #define HZLOCKKEY S_IRUSR|S_IWUSR // Semaphore settings for 'lock key'
class _lockset { // Comprises a set of 32 semaphores - even though most applications will frequently either use only a few or quite large numbers of them. They are grouped // in blocks of 32 because they can be set and tested by bitwise operations and giving 32 at a time reduces demand on system resorces.
void _init (void) { // Initializes the set of 32 semaphores // // Arguments: None // Returns: None
_hzfunc("_lock::_init") ;
if ((m_nSemId = semget(IPC_PRIVATE, 32, HZLOCKNOKEY)) < 0) hzexit(E_INITFAIL, "SERIOUS ERROR Could not create semaphore\n") ;
if (semctl(m_nSemId, 0, SETVAL, 0) == -1) hzexit(E_INITFAIL, "Problem initializing semaphore id=%d", m_nSemId) ; }
void _halt (void) { // Release all resources back to operating system // // Arguments: None // Returns: None
_hzfunc("_lock::_halt") ;
if (semctl(m_nSemId, 0, IPC_RMID) < 0) hzerr(E_SHUTDOWN, "Cannot free semaphore (%d)", m_nSemId) ; }
public: _lockset* next ; // Next lock in the series uint32_t m_bFree ; // Bitwise free list int32_t m_nSemId ; // Unique id for semahore group
_lockset (void) { m_bFree = 0xffffffff ; next = 0 ; _init() ; }
~_lockset (void) { _halt() ; } } ;
class _lockmgr { // Manages all issued sets of semaphores
_lockset* s_pSemaGroups ; // Allocated semaphore groups _lockset* s_pSemaLast ; // Last semaphore group in series
void _close (void) { _lockset* pl ; // Semaphone lock set iterator _lockset* plnext ; // Next semaphore lock set in series
for (pl = s_pSemaGroups ; pl ; pl = plnext) { plnext = pl->next ; delete pl ; } }
public: _lockmgr () { s_pSemaGroups = 0 ; s_pSemaLast = 0 ; }
~_lockmgr () { _close() ; }
hzEcode Alloc (int32_t& semid, uint32_t& resource) ; hzEcode Free (int32_t semid, uint32_t resource) ; } ;
hzEcode _lockmgr::Alloc (int32_t& semid, uint32_t& resource) { _hzfunc("_lockmgr::Alloc") ;
_lockset* pl ; // Current lock set (of 32 locks) uint32_t div ; // Lock set iterator mask uint32_t nIndex ; // Lock set bitwise iterator
for (pl = s_pSemaGroups ; pl ; pl = pl->next) { if (pl->m_bFree) { for (div = 1, nIndex = 0 ; nIndex < 32 ; div *= 2, nIndex++) { if (pl->m_bFree & div) { pl->m_bFree &= ~div ; semid = pl->m_nSemId ; resource = nIndex ; //cout << "Got lock with m_nSemId of " << m_nSemId << " and resource of " << m_nResource << endl ; return E_OK ; } } } }
// None free, allocate a new semaphore group and take the first slot
pl = new _lockset() ; if (!pl) hzexit(E_MEMORY, "Could not allocate a _lockset") ;
pl->m_bFree = 0xfffffffe ;
if (!s_pSemaGroups) s_pSemaGroups = s_pSemaLast = pl ; else { s_pSemaLast->next = pl ; s_pSemaLast = pl ; }
semid = pl->m_nSemId ; resource = 0 ; return E_OK ; }
hzEcode _lockmgr::Free (int32_t semid, uint32_t resource) { _hzfunc("_lockmgr::Free") ;
_lockset* pl ; // Current lock set (of 32 locks) uint32_t div ; // Lock set iterator mask uint32_t nIndex ; // Lock set bitwise iterator
for (pl = s_pSemaGroups ; pl ; pl = pl->next) { if (semid != pl->m_nSemId) continue ;
for (div = 1, nIndex = 0 ; nIndex < resource ; div *= 2, nIndex++) ; pl->m_bFree |= div ; return E_OK ; }
return hzerr(E_CORRUPT, "Could not located sema group with semid of %d\n", semid) ; }
/* ** Variables */
static _lockmgr theLockMgr ; // Global semaphore
/* ** Section 2: hzSysLock functions */
void hzSysLock::_init (void) { // Obtains a semaphore id for the semaphore set and a resource id (both needed for Lock/Unlock) // // Arguments: None // Returns: None
theLockMgr.Alloc(m_nSemId, m_nResource) ; }
void hzSysLock::_halt (void) { // Releases semaphore // // Arguments: None // Returns: None
theLockMgr.Free(m_nSemId, m_nResource) ; }
void hzSysLock::Lock (void) { // Lock semaphore // // Arguments: None // Returns: None
_hzfunc("hzSysLock::Lock") ;
sembuf lockbuf [2] ; // Semaphore operation buffer int32_t tid ; // Caller thread id
tid = pthread_self() ;
if (m_nSemId == -1) hzexit(E_NOTFOUND, "Attempting to lock an uninitialized semaphore") ; //return ;
if (m_lockval && m_lockval == tid) { fprintf(stderr, "thread %u re-locking sema %d:%d\n", tid, m_nResource + 1, m_nSemId) ; m_count++ ; } else { lockbuf[0].sem_num = m_nResource ; lockbuf[0].sem_op = 0 ; lockbuf[0].sem_flg = 0 ; lockbuf[1].sem_num = m_nResource ; lockbuf[1].sem_op = 1 ; lockbuf[1].sem_flg = 0 ;
fprintf(stderr, "thread %u seeking sema %d:%d\n", tid, m_nResource + 1, m_nSemId) ;
if (semop(m_nSemId, &lockbuf[0], 2) == -1) hzexit(E_INITFAIL, "lock on sema %d [%d] could not be obtained\n", m_nResource + 1, m_nSemId) ;
fprintf(stderr, "thread %u obtained sema %d:%d\n", tid, m_nResource + 1, m_nSemId) ;
m_lockval = tid ; m_count = 1 ; } }
void hzSysLock::Unlock (void) { // Unlock semaphore // // Arguments: None // Returns: None
_hzfunc("hzSysLock::Unlock") ;
sembuf lockbuf ; // Semaphore operation buffer int32_t tid ; // Caller thread id
tid = pthread_self() ;
if (m_nSemId == -1) return ;
if (!m_lockval) hzexit(E_CORRUPT, "Attempting to unlock a semaphore that is not locked by any thread") ;
if (m_lockval != tid) hzexit(E_CORRUPT, "Attempting to unlock a semaphore that is locked by another thread (%u)", m_lockval) ;
m_count-- ; if (m_count > 0) fprintf(stderr, "thread %u unwinding sema %d:%d\n", tid, m_nResource + 1, m_nSemId) ; if (m_count == 0) { m_lockval = 0 ; lockbuf.sem_num = m_nResource ; lockbuf.sem_op = -1 ; lockbuf.sem_flg = 0 ;
fprintf(stderr, "thread %u releasing sema %d:%d\n", tid, m_nResource + 1, m_nSemId) ;
if (semop(m_nSemId, &lockbuf, 1) == -1) hzerr(E_RELEASE, "Unlocked sema %d [%d]\n", m_nResource, m_nSemId) ; } }