Click here to Skip to main content
15,885,216 members
Articles / Programming Languages / C++

Shared and Exclusive lock control

Rate me:
Please Sign up or sign in to vote.
3.05/5 (7 votes)
31 Aug 20042 min read 44.2K   629   11  
C++ class for shared and exclusive lock control.
#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();
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
Application and systems programmer since 1972.
Mostly mainframe programming - Assembler, PL/X.
Products: IBM: DFSMSHSM, Candle Corp: Omegamon (IMS and MVS), PKWARE: SecureZIP


Dabble in Windows and Linux at home - C++ and Intel Assembler.

Comments and Discussions