//************************************************************************* // 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.hpp" // related header #include "kernel.hpp" // 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 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 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; UINT16 idx; for ( 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; UINT16 idx; for (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 } // // 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 ++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 ++idx; } idx = 0; uBit = 1; uBitCpy = uSidMask; while (uBitCpy) { if (uBitCpy & uBit) { arrSem[idx].SemWait(arrPCB[uPid]); arrPCB[uPid].uSemWait |= uBit; } uBitCpy &= ~uBit; uBit ++idx; } Block(); } return 0; } /////////////////////////////////// eof ////////////////////////////////////