#include "locker.h"
#include <assert.h>
//-------------------------------------------
// Constructor
//-------------------------------------------
Locker::Locker()
{
locks=NULL; // Initially: No locks
Ex=new EXCLUSIVE_EX(); // Setup thread serialization
}
//-------------------------------------------
// Destructor
//-------------------------------------------
Locker::~Locker()
{
PLOCKLIST pl, nxtpl;
Ex->Get(); // Exclusive execution ON
pl=locks; // Scan for any active locks.
// If found, USER PROGRAMMING ERROR,
// Destructor was called while some thread is waiting
// on a lock. Would cause program check in that
// thread if the destructor completed.
// Terminate the PROCESS.
while (pl!=NULL)
{
if (pl->users!=NULL)
assert(0);
nxtpl=pl->next;
delete pl;
pl=nxtpl;
}
locks=NULL;
Ex->Release(); // Exclusive execution OFF
delete Ex;
}
//-------------------------------------------
// Obtain a SHARED lock
//-------------------------------------------
int Locker::GetSharedLock(const void *lname, int lnamel)
{
int rc=LOCK_OK;
PLOCKUSER pu;
if (lnamel>MAXLOCKNAME)
return LOCK_ERROR;
ProcIdentity *pid=new ProcIdentity();
Ex->Get(); // Exclusive execution ON
pu=FindLock(lname, lnamel, pid);
if (pu!=NULL) // Thread already has the lock
{
if (pu->locktype) rc=LOCK_EXCL; // Return held lock type
else rc=LOCK_SHARED;
Ex->Release(); // Exclusive execution OFF
}
else
InsertLock(lname,lnamel,LOCK_SHARED, pid); // May suspend thread
// Releases CPU control
return rc;
}
//-------------------------------------------
// Obtain an EXCLUSIVE lock
//-------------------------------------------
int Locker::GetExclLock(const void *lname, int lnamel)
{
int rc=LOCK_OK;
PLOCKUSER pu;
if (lnamel>MAXLOCKNAME)
return LOCK_ERROR;
ProcIdentity *pid=new ProcIdentity();
Ex->Get(); // Exclusive execution ON
pu=FindLock(lname, lnamel, pid);
if (pu!=NULL) // Thread already has the lock
{
if (pu->locktype) rc=LOCK_EXCL; // Return held lock type
else rc=LOCK_SHARED;
Ex->Release();
}
else
InsertLock(lname,lnamel,LOCK_EXCL,pid); // May suspend thread
// Releases CPU control
return rc;
}
//-------------------------------------------
// Release a lock
//-------------------------------------------
int Locker::RelLock(const void *lname, int lnamel)
{
int rc=LOCK_OK;
PLOCKLIST pl;
PLOCKUSER p;
BOOL pulse=FALSE;
if (lnamel>MAXLOCKNAME)
return LOCK_ERROR;
Ex->Get(); // Exclusive execution ON
pl=FindL(lname,lnamel); // Find the thread's lock entry
if (pl==NULL)
{
Ex->Release(); // Release CPU control
return LOCK_NOT_HELD; // None, error
}
p=pl->users; // Head of lock list
ProcIdentity *pid=new ProcIdentity();
while (p!=NULL)
{
if (pid->Compare(p->pid)) break;
p=p->next;
}
if (p==NULL) // Didn't find it, error
{
delete pid;
Ex->Release(); // Release CPU control
return LOCK_NOT_HELD; // None, error
}
if (p->prev==NULL) // We're at the head of the chain.
// Need to pulse semaphore to release any
// waiting threads.
{
pulse=TRUE; // Will pulse if not last user
pl->users=p->next; // Remove this thread's lock entry
if (p->next!=NULL)
p->next->prev=NULL;
}
else
{
p->prev->next=p->next; // Remove this thread's lock entry
if (p->next!=NULL)
p->next->prev=p->prev;
}
delete pid;
delete p->pid;
delete p; // Delete the lock entry
/*
if (pl->users==NULL) // No users of this lock any more
{ // Remove it from the lock list.
pulse=FALSE; // No pulse.. no one is using it.
if (pl->prev==NULL && pl->next==NULL)
locks=NULL;
else
{
if (pl->prev!=NULL)
pl->prev->next=pl->next;
if (pl->next!=NULL)
pl->next->prev=pl->prev;
}
delete pl->Semaphore; // Delete the lock's semaphore
delete pl; // Delete the lock
}
*/
if (pulse) // need pulse
pl->Semaphore->UnBlock(); // This releases ALL threads waiting in
// WaitForLock
Ex->Release(); // Release CPU control
return rc;
}
//----------------------------------
// Test if lock is held by this thread.
//----------------------------------
int Locker::TestLock(const void *lname, int lnamel)
{
int rc=LOCK_OK;
if (lnamel>MAXLOCKNAME)
return LOCK_ERROR;
ProcIdentity *pid=new ProcIdentity();
Ex->Get(); // Get CPU control
PLOCKUSER p=FindLock(lname,lnamel,pid); // Look for lock
if (p==NULL)
rc=LOCK_NOT_HELD; // Not there, return error code
else
rc=p->locktype; // There, return lock type
Ex->Release(); // Release CPU control
delete pid;
return rc;
}
//----------------------------------
// Private: Find LOCKNAME list
// Returns PLOCKLIST if found, or NULL if not.
//----------------------------------
Locker::PLOCKLIST Locker::FindL(const void *lname, int lnamel)
{
PLOCKLIST pl=locks;
if (pl==NULL) return NULL;
while (pl!=NULL) // Scan lock list
{
if (pl->lnamel==lnamel) // Length equal?
if (memcmp((void *)pl->lname,lname,lnamel)==0) // Compare names
break; // Equal, found it
pl=pl->next; // No, skip
}
return pl;
}
//-------------------------------------------
// Find a lock owned by the executing thread.
// Returns PLOCKUSER if found, or NULL if not.
//-------------------------------------------
Locker::PLOCKUSER Locker::FindLock(const void *lname, int lnamel, ProcIdentity *pid)
{
PLOCKLIST pl=NULL;
PLOCKUSER p=NULL;
pl=FindL(lname,lnamel); // Find LOCKLIST
if (pl==NULL) return NULL; // None, return NULL
p=pl->users; // Scan lock users
while (p!=NULL)
{
if (pid->Compare(p->pid)) break;
p=p->next;
}
return p;
}
//--------------------------------------------
// Make a LOCKNAME entry
// Called when FindL returns NULL to
// InsertLock
// LOCKLIST is chained at the end of the locks list
// and a pointer to it is returned.
//--------------------------------------------
Locker::PLOCKLIST Locker::MakeL(const void *lname, int lnamel)
{
PLOCKLIST pl;
PLOCKLIST plocks=locks;
pl=new LOCKLIST_;
memset((void *)pl,0,sizeof(LOCKLIST_));
memcpy((void *)pl->lname,(void *)lname,lnamel);
pl->lnamel=lnamel;
pl->Semaphore=new Locker::Semaphore(); // Initially unblocked semaphore
if (plocks==NULL) // First lock
{
locks=pl;
return pl;
}
while (plocks->next!=NULL) plocks=plocks->next; // Scan to EOL
plocks->next=pl; // Chain it in
pl->prev=plocks;
return pl;
}
//--------------------------------------------
// Insert a lock into the list,
// Wait for ownership if:
// The lock being inserted is EXCLUSIVE
// or
// An EXCLUSIVE lock exists in the list.
// Exclusive use of the CPU is released within
// this function.
//--------------------------------------------
void Locker::InsertLock(const void *lname, int lnamel, int locktype, ProcIdentity *pid)
{
PLOCKLIST pl;
PLOCKUSER pu, pul;
pl=FindL(lname,lnamel); // Find the LOCKLIST
if (pl==NULL)
pl=MakeL(lname,lnamel); // None, make a new one
pu=new LOCKUSER_; // Setup thread lock entry
memset((void *)pu,0,sizeof(LOCKUSER_));
pu->pid=pid;
pu->locktype=locktype;
pul=pl->users; // Chain it at EOL
if (pul==NULL) // First lock on the chain. No need to wait
{
pl->users=pu;
if (locktype)
pl->Semaphore->Block();
Ex->Release();
}
else
{ // Existing locks, wait if any EXCLUSIVE,
// including this one.
while (pul->next!=NULL)
pul=pul->next;
pu->prev=pul;
if (pul->next!=NULL)
pul->prev=pu;
pul->next=pu;
WaitForLock(pl,pid,locktype); // Wait for lock (maybe)
}
return;
}
//--------------------------------------------
// Wait for a lock
// Waits for ownership of a lock if:
// Any EXCLUSIVE lock already exists.
// This lock is exclusive, and any lock exists.
// RELEASES CPU CONTROL
//--------------------------------------------
void Locker::WaitForLock(PLOCKLIST pl, ProcIdentity *pid, int locktype)
{
BOOL needwait=TRUE;
PLOCKUSER p;
while (needwait) // Loop while wait MAY BE required
{
p=pl->users;
needwait=FALSE;
while (p!=NULL)
{
if (pid->Compare(p->pid)) break;
if (p->locktype==LOCK_EXCL || locktype==LOCK_EXCL)
{
needwait=TRUE;
Ex->Release(); // Release CPU control while waiting
pl->Semaphore->Wait();
Ex->Get(); // Get CPU control
pl->Semaphore->Block(); // Re-block the semaphore
p=pl->users; // Reset to head of user list
continue;
}
p=p->next;
}
}
Ex->Release();
}