ipc.cc

//*************************************************************************
//  MODULE : IPC - Inter Process Communication module			  *
//  AUTHOR : Ron Chernich                                                 *
//  PURPOSE: This module contains the Kernel IPC functions and the	  *
//	     supporting object methods, such as semaphores.		  *
//  HISTORY:                                                              *
//    08-MAR-94 Split off from Exec module				  *
//    13-APR-94 Semaphore count initialisation added			  *
//    19-APR-94 SemDelUser must NULL pst to prevent bad deletes by ::dtor *
//*************************************************************************

#include "exec.hh"	    // related header
#include "kernel.hh"	    // can't put this in header due recursion


///////////////////////////////////////////////////////////////////////////
//		   Class Methods for Semaphores
//-------------------------------------------------------------------------
// Constructor must Init the queues and null the name .. destructor will
// cleanup any memory in use (in case of a dis-orderly shut-down)
//
Sem4::Sem4 (void) : uPid(FALSE), uDelayed(TRUE), pst(NULL), uCount(0)  { }

Sem4::~Sem4 (void)
{
  if (pst)
   DELETE_ARRAY pst;
}

///////////
// Open the semaphore for use and set its count and (optional) name string.
// The passed PID is added to the user stack and saved as the "creator".
//
void Sem4::SemOpen (PCB &proc, UINT16 uCnt, char *pszName)
{
  if (this->SemIsFree()) {
    uCount = uCnt;
    uCreator = proc.uPid;
    if (pszName && strlen(pszName))
      if (pst = new char[strlen(pszName)+1])
        strcpy(pst, pszName);
    this->SemAddUser(proc);
  }
}
        
////////////////
// Allows us to use the "==" operator on a semaphore reference to test its
// name string.
// RETURNS: TRUE  .. it's a match
//          FALSE .. different
//
BOOL Sem4::operator == (char *pszName)
{
  if (pst && (strcmp(pst, pszName) == 0))
    return TRUE;
  return FALSE;
}

////////
// The semaphore is no longer in use when the PID queue is empty
// RETURNS: TRUE  .. yes it is
//	    FALSE .. no it ain't
//
BOOL Sem4::SemIsFree (void)
{
  return uPid.PqEmpty();
}

///////
// Check if the passed PID is using the semaphore
// RETURNS: TRUE  .. yes it is
//	    FALSE .. no it ain't
//
BOOL Sem4::SemIsUser (PCB &proc)
{
  return uPid.PqFind(proc.uPid);
}

///////
// Adds the passed process to the list of users of this semaphore
// RETURNS: TRUE  .. User added
//	    FALSE .. user was already a user!
//
BOOL Sem4::SemAddUser (PCB &proc)
{
  if (!uPid.PqFind(proc.uPid)) {
    uPid.PqAdd(proc);
    return TRUE;
  }
  return FALSE;
}

/////////////
// Remove passed PID from stack of registered users and the queue of
// delayed processes (if it's there).  If the stack becomes empty,
// the semaphore is "free" and its name (if any) is removed.
// RETURNS: TRUE  .. User removed
//	    FALSE .. user was not a user!
//
BOOL Sem4::SemDelUser (PCB &proc)
{
  UINT16 uTemp = uPid.PqGet(proc.uPid);
  if (uTemp == proc.uPid)  {
    if (uDelayed.PqFind(proc.uPid))
      uDelayed.PqGet(proc.uPid);
    if (uPid.PqEmpty() && pst) {
      DELETE_ARRAY pst;
      pst = NULL;
    }
    return TRUE;
  }
  return FALSE;
}

///////////
// The passed PID wants to gain access to the semaphore.  Provided the
// current count is > zero, just decrement the count and return TRUE.  If
// the semaphore is fully in use, add the user PID# to the priority queue
// and indicate this to the caller by returning FALSE.
// RETURNS: TRUE  .. PID has exclusive use of resource
//          FALSE .. PID added to waiting PID queue
//
BOOL Sem4::SemWait (PCB &proc)
{
  if (uCount) {
    --uCount;
    return TRUE;
  }
  uDelayed.PqAdd(proc);
  return FALSE;
}

//////////
// Release a lock on the semaphore, incrementing the semaphore counter.
// RETURNS: TRUE  .. there are processes waiting for a lock
//          FALSE .. semaphore is temporarily "free"
//
BOOL Sem4::SemSignal (void)
{
  ++uCount;
  return (uDelayed.PqEmpty() ? FALSE : TRUE);  
}

/////////////
// Removes and returns the PID# at the head of the "delayed" queue.
// RETURNS: PID#, or NO_PROC if queue empty.
//
UINT16 Sem4::SemGetDelayed (void)
{
  return uDelayed.PqGet();
}


/////////////
// Removes and returns the PID# from the "delayed" queue.
// RETURNS: PID#, or NO_PROC if queue empty.
//
UINT16 Sem4::SemGetDelayed (UINT16 uPid)
{
  return uDelayed.PqGet(uPid);
}


///////////////////////////////////////////////////////////////////////////
//		   Exec Class Methods for IPC operations
//-------------------------------------------------------------------------
// Search the list of semaphores in use to see if any has the user
// supplied name as passed.  If found, add the current process to its
// list of users.
// RETURNS: Zero for "not found" or semaphore ID.
//
UINT16 Exec::IpcOpen (char *pszName)
{
  for (UINT16 idx = 0; idx < MAX_SEM; idx++)
    if (!arrSem[idx].SemIsFree() && (arrSem[idx] == pszName)) {
      arrSem[idx].SemAddUser(arrPCB[uCurProc]);
      UINT16 uSid = 0x01 << idx;
      arrPCB[uCurProc].uSemOpen |= uSid;
      return uSid;
    }
  return 0;
}

///////////
// Walk the semaphore array to locate the "lowest" unused one and allocate
// it to the currently active process.  The passed value is used to set the
// count (unity implies a binary semaphore). If the passed string pointer is
// non-null, "name" the semaphore with it.
// RETURNS: Semaphore ID, or
//          Zero if none are free or supplied name not unique
//
UINT16 Exec::IpcAlloc (char *pszName, UINT16 nInit)
{
  for (UINT16 idx = 0; idx < MAX_SEM; idx++)
    if (arrSem[idx].SemIsFree()) {
      arrSem[idx].SemOpen(arrPCB[uCurProc], nInit, pszName);
      UINT16 uSid = 0x01 << idx;
      arrPCB[uCurProc].uSemOpen |= uSid;
      return uSid;
    }
  return 0;  
}

/////////////
// The passed process wishes to close/free/release the semaphore with
// the passed ID. We can't assume that the process is the current one
// because this call could be as a result of an operator DElete action.
//
// As a result, another process may be able to acquire the semaphore.
// If so, unblock it and remove it from all other queues it may also
// have been patiently waiting in.
//
void Exec::IpcClose (UINT16 uPid, UINT16 uSid)
{
  if (arrPCB[uPid].uSemOpen & uSid) {
    UINT16 uBit = uSid;
    for (UINT16 idx = 0; !(uBit & 0x01); ++idx)
      uBit >>= 0x01;
    arrSem[idx].SemDelUser(arrPCB[uPid]);
    arrPCB[uPid].uSemOpen &= ~uSid;
    arrPCB[uPid].uSemWait &= ~uSid;
    if (arrPCB[uPid].uSemSet & uSid) {
      arrPCB[uPid].uSemSet &= ~uSid;
      UINT16 uHead = arrSem[uSid].SemGetDelayed();
      if (uHead != NO_PROC) {
        arrSem[idx].SemWait(arrPCB[uHead]);
        arrPCB[uHead].uSemSet |= uHead;
        arrPCB[uHead].uSemWait = 0;
        MSG msg(ID_Kernel, KM_Signal, uSid);
        PostReply(uPid, &msg);
      }
    }
  }
}

///////////////
// The passed PID has SIGNALED that is has completed the mutual exclusion
// action associated with the passed semaphore.  If the "delayed" priority
// queue is empty, we simply continue - otherwise we remove the process
// at the head of the queue for this semaphore and re-issue the 
// call for it - causing it to unblock and clean itself up for execution.
//
void Exec::IpcSignal (UINT16 uPid, UINT16 uSid)
{
  if (arrPCB[uPid].uSemOpen & uSid) {
    UINT16 uBit = uSid;
    for (UINT16 idx = 0; !(uBit & 0x01); ++idx)
      uBit >>= 0x01;
    arrPCB[uPid].uSemSet &= ~uSid;
    if (arrSem[idx].SemSignal())
      IpcWait(arrSem[idx].SemGetDelayed(), uSid);
  }
}

///////////
// This function allows multiple wait requests by logical disjunction of
// semaphore ID numbers (powers of 2).  We must validate that all requests
// are for resources the process actually has open (returning -1 if ANY are
// invalid). There are then three cases to consider:
//
// 1. The semaphore(s) is NOT available. This should only arise when the
//    process is running. For each bit set in , place the PID
//    on the waiting queue, setting the corresponding PID bit-map. Finally,
//    block the process and return (0).
//    
// 2. A semaphore is available and the process has not "delayed" on it:
//    Add the PID to the semaphores "user" list, decrementing its count.
//    Flag the PID to show it has set this semaphore and return the ID of
//    the semaphore the process now has possession of (perhaps non-exclusive)
//
// 3. The process is currently blocked and a semaphore is now available:
//    This should only arise via a call from , having determined
//    that the delayed process is next in line for the passed specific
//    semaphore.  will have removed the PID from the "delayed"
//    queue, so add it to the "user" list, set the "set by me" PID bit-map
//    bit, then check the "I've been waiting on" bit-map.  If this semaphores
//    ID was NOT the only one, we must remove the PID from the "delayed"
//    queues of all the others. Then we need to cause the process to unblock
//    by creating a message structure which can be attached to the PID as the
//    reply. Finally, we can clear the "waiting on" bit-map and return the
//    ID the process now has posession of.
//
// RETURNS: Negative .. -1 indicates process has not opened the semaphore!
//          Positive .. Id of semaphore process now has possesion of.
//              Zero .. semaphore in use, process now blocked.
//
INT16 Exec::IpcWait (UINT16 uPid, UINT16 uSidMask)
{
  UINT32 uVal = 1;
  while (uVal < (UINT32)arrPCB[uPid].uSemOpen) {
    if ((UINT16)(uVal & 0xffff) & uSidMask)
      if (!(arrPCB[uPid].uSemOpen & (UINT16)uVal))
        return -1;
    uVal <<= 0x01;
  }
  //
  // All requests valid, process previously blocked process first
  //
  if (arrPCB[uPid].uSemWait) {
    UINT16 idx = 0;
    UINT16 uBit = 1;
    while (arrPCB[uPid].uSemWait) {
      if (uBit == uSidMask)
        arrSem[idx].SemWait(arrPCB[uPid]);
      if (arrPCB[uPid].uSemWait & uBit)
        arrSem[idx].SemGetDelayed(uPid);
      arrPCB[uPid].uSemWait &= ~uBit;
      uBit <<= 0x01;
      ++idx;  
    }
    arrPCB[uPid].uSemSet |= uSidMask;
    MSG msg(ID_Kernel, KM_Signal, uSidMask);
    PostReply(uPid, &msg);
    return uSidMask;
  }
  //
  // process all possession requests until one is granted, or all block
  // If one is granted, unwind any that blocked.
  //
  if ((uPid == uCurProc) && (arrPCB[uPid].uSemWait == 0)) {
    UINT16 idx = 0;
    UINT16 uBit = 1;
    UINT16 uBitCpy = uSidMask;
    while (uBitCpy) {
      if ((uBitCpy & uBit) && arrSem[idx].SemGetCount()) {
        arrSem[idx].SemWait(arrPCB[uPid]);
        arrPCB[uPid].uSemSet |= uBit;
        return uBit;
      }
      uBitCpy &= ~uBit;
      uBit <<= 0x01;
      ++idx;
    }
    idx = 0;
    uBit = 1;
    uBitCpy = uSidMask;
    while (uBitCpy) {
      if (uBitCpy & uBit) {
        arrSem[idx].SemWait(arrPCB[uPid]);
        arrPCB[uPid].uSemWait |= uBit;
      }
      uBitCpy &= ~uBit;
      uBit <<= 0x01;
      ++idx;
    }
    Block();
  }
  return 0;
}

/////////////////////////////////// eof ////////////////////////////////////