// Synch.cc // Routines for synchronizing threads. Three kinds of // synchronization routines are defined here: semaphores, locks // and condition variables (the implementation of the last two // are left to the reader). // // Any implementation of a synchronization routine needs some // primitive atomic operation. We assume Nachos is running on // a uniprocessor, and thus atomicity can be provided by // turning off interrupts. While interrupts are disabled, no // context switch can occur, and thus the current thread is guaranteed // to hold the CPU throughout, until interrupts are reenabled. // // Because some of these routines might be called with interrupts // already disabled (Semaphore::V for one), instead of turning // on interrupts at the end of the atomic operation, we always simply // re-set the interrupt state back to its original value (whether // that be disabled or enabled). // // Copyright (c) 1992-1993 The Regents of the University of California. // All rights reserved. See copyright.h for copyright notice and limitation // of liability and disclaimer of warranty provisions. #include "copyright.h" #include "synch.h" #include "system.h" #include "list.h" //---------------------------------------------------------------------- // Semaphore::Semaphore // Initialize a semaphore, so that it can be used for synchronization. // // "debugName" is an arbitrary name, useful for debugging. // "initialValue" is the initial value of the semaphore. //---------------------------------------------------------------------- Semaphore::Semaphore(char* debugName, int initialValue) { name = debugName; value = initialValue; queue = new List; } //---------------------------------------------------------------------- // Semaphore::~Semaphore // De-allocate semaphore, when no longer needed. Assume no one // is still waiting on the semaphore! //---------------------------------------------------------------------- Semaphore::~Semaphore() { delete queue; } //---------------------------------------------------------------------- // Semaphore::P // Wait until semaphore value > 0, then decrement. Checking the // value and decrementing must be done atomically, so we // need to disable interrupts before checking the value. // // Note that Thread::Sleep assumes that interrupts are disabled // when it is called. //---------------------------------------------------------------------- void Semaphore::P() { IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts while (value == 0) { // semaphore not available queue->Append((void *)currentThread); // so go to sleep currentThread->Sleep(); } value--; // semaphore available, // consume its value (void) interrupt->SetLevel(oldLevel); // re-enable interrupts } //---------------------------------------------------------------------- // Semaphore::V // Increment semaphore value, waking up a waiter if necessary. // As with P(), this operation must be atomic, so we need to disable // interrupts. Scheduler::ReadyToRun() assumes that threads // are disabled when it is called. //---------------------------------------------------------------------- void Semaphore::V() { Thread *thread; IntStatus oldLevel = interrupt->SetLevel(IntOff); thread = (Thread *)queue->Remove(); if (thread != NULL) // make thread ready, consuming the V immediately scheduler->ReadyToRun(thread); value++; (void) interrupt->SetLevel(oldLevel); } // Modified by O'D (added function bodies) //---------------------------------------------------------------------- // Lock::Lock // Initialize a lock in FREE status. //---------------------------------------------------------------------- Lock::Lock(char* debugName) { name = debugName; currentStatus = FREE; holdingThread = NULL; queue = new List; } //---------------------------------------------------------------------- // Lock::~Lock // De-allocate the queue for the lock. This code assumes that // there is no thread on the queue. //---------------------------------------------------------------------- Lock::~Lock() { delete queue; } //---------------------------------------------------------------------- // Lock::Acquire // Wait until the lock is FREE, then set it BUSY. //---------------------------------------------------------------------- void Lock::Acquire() { IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts while (currentStatus != FREE) { // lock not available DEBUG('t', "Lock::Acquire - thread \"%s\" waiting on lock \"%s\".\n", currentThread->getName(), name); queue->Append((void *)currentThread); // so go to sleep currentThread->Sleep(); } DEBUG('t', "Lock::Acquire - thread \"%s\" acquiring lock \"%s\".\n", currentThread->getName(), name); currentStatus = BUSY; // lock available, holdingThread = currentThread; // set it to BUSY (void) interrupt->SetLevel(oldLevel); // re-enable interrupts } //---------------------------------------------------------------------- // Lock::Release // Set lock FREE, and wake up one thread from the queue if there // is one. //---------------------------------------------------------------------- void Lock::Release() { Thread *thread; IntStatus oldLevel = interrupt->SetLevel(IntOff); if (isHeldByCurrentThread()) { DEBUG('t', "Lock::Release - thread \"%s\" releasing lock \"%s\".\n", currentThread->getName(), name); thread = (Thread *)queue->Remove(); if (thread != NULL) // make thread ready, set status FREE scheduler->ReadyToRun(thread); currentStatus = FREE; holdingThread = NULL; } (void) interrupt->SetLevel(oldLevel); } //---------------------------------------------------------------------- // Lock::isHeldByCurrentThread // Returns true if and only if the lock is held by the running // thread. //---------------------------------------------------------------------- bool Lock::isHeldByCurrentThread() { return (currentThread == holdingThread); } //---------------------------------------------------------------------- // Condition::Condition //---------------------------------------------------------------------- Condition::Condition(char* debugName, Lock *conditionLock) { name = debugName; queue = new List; lock = conditionLock; } //---------------------------------------------------------------------- // Condition::~Condition //---------------------------------------------------------------------- Condition::~Condition() { delete queue; } //---------------------------------------------------------------------- // Condition::Wait //---------------------------------------------------------------------- void Condition::Wait() { IntStatus oldLevel = interrupt->SetLevel(IntOff); if (lock->isHeldByCurrentThread()) { lock->Release(); DEBUG('t', "Condition::Wait - thread \"%s\" waiting on condition \"%s\" for lock \"%s\".\n", currentThread->getName(), name, lock->getName()); queue->Append((void *)currentThread); currentThread->Sleep(); DEBUG('t', "Condition::Wait - thread \"%s\" waking from condition \"%s\" for lock \"%s\".\n", currentThread->getName(), name, lock->getName()); lock->Acquire(); } (void) interrupt->SetLevel(oldLevel); } //---------------------------------------------------------------------- // Condition::Signal //---------------------------------------------------------------------- void Condition::Signal() { Thread *thread; IntStatus oldLevel = interrupt->SetLevel(IntOff); if (lock->isHeldByCurrentThread()) { DEBUG('t', "Condition::Signal - thread \"%s\" signalling condition \"%s\" for lock \"%s\".\n", currentThread->getName(), name, lock->getName()); thread = (Thread *)queue->Remove(); if (thread != NULL) // make thread ready, set status FREE scheduler->ReadyToRun(thread); } (void) interrupt->SetLevel(oldLevel); } //---------------------------------------------------------------------- // Condition::Broadcast //---------------------------------------------------------------------- void Condition::Broadcast() { } //---------------------------------------------------------------------- // Condition::getLock //---------------------------------------------------------------------- Lock* Condition::getLock() { return lock; } // End O'D // New definitions by O'D //---------------------------------------------------------------------- // BoundedBuffer::BoundedBuffer //---------------------------------------------------------------------- BoundedBuffer::BoundedBuffer(char *debugName, int bufferSize) { name = debugName; size = bufferSize; contents = (char *) calloc(size, sizeof(char)); nextItem = 0; nextBlank = 0; notEmpty = new Semaphore("notEmpty semaphore", 0); notFull = new Semaphore("notFull semaphore", size); lock = new Semaphore("lock semaphore", 1); } //---------------------------------------------------------------------- // BoundedBuffer::~BoundedBuffer //---------------------------------------------------------------------- BoundedBuffer::~BoundedBuffer() { free(contents); notEmpty->~Semaphore(); notFull->~Semaphore(); lock->~Semaphore(); } //---------------------------------------------------------------------- // BoundedBuffer::Read //---------------------------------------------------------------------- char BoundedBuffer::Read() { char result; notEmpty->P(); lock->P(); result = contents[nextItem]; nextItem = (nextItem+1) % size; lock->V(); notFull->V(); return(result); } //---------------------------------------------------------------------- // BoundedBuffer::Write //---------------------------------------------------------------------- void BoundedBuffer::Write(char item) { notFull->P(); lock->P(); contents[nextBlank] = item; nextBlank = (nextBlank+1) % size; lock->V(); notEmpty->V(); } //---------------------------------------------------------------------- // Channel::Channel //---------------------------------------------------------------------- Channel::Channel(char *debugName) { name = debugName; lock = new Lock("channel lock"); clearToSend = new Condition("channel clear for send", lock); clearToReceive = new Condition("channel clear to receive", lock); readyToSend = new Condition("channel message in buffer", lock); readyToReceive = new Condition("channel receiver ready", lock); } //---------------------------------------------------------------------- // Channel::~Channel //---------------------------------------------------------------------- Channel::~Channel() { clearToReceive->~Condition(); clearToSend->~Condition(); readyToSend->~Condition(); readyToReceive->~Condition(); lock->~Lock(); } //---------------------------------------------------------------------- // Channel::Send //---------------------------------------------------------------------- void Channel::Send(char messageToSend, int waitForReceive) { // char *messagePtr = (char *)malloc(sizeof(char)); lock->Acquire(); DEBUG('y', "Trying to send %c\n", messageToSend); while (sendBusy) { DEBUG('y', "Waiting for clearance to send %c\n", messageToSend); clearToSend->Wait(); } DEBUG('y', "Entering %c in sendBuffer\n", messageToSend); sendBusy = 1; sendBuffer = messageToSend; sendBufferFull = 1; readyToSend->Signal(); while (!receiveToken) { DEBUG('y', "Waiting for receiver for %c\n", messageToSend); readyToReceive->Wait(); } DEBUG('y', "Sent %c\n", messageToSend); receiveToken = 0; // indicate receipt of null message // from receiver acknowledging real // message if (!sendBufferFull) { // if the message has already been received DEBUG('y', "Completed sending %c\n", messageToSend); sendBusy = 0; receiveBusy = 0; clearToSend->Signal(); clearToReceive->Signal(); } lock->Release(); } //---------------------------------------------------------------------- // Channel::Receive //---------------------------------------------------------------------- char Channel::Receive() { char messageReceived; lock->Acquire(); DEBUG('y', "Trying to receive\n"); while (receiveBusy) { DEBUG('y', "Waiting for clearance to receive\n"); clearToReceive->Wait(); } DEBUG('y', "Clear to receive\n"); receiveBusy = 1; receiveToken = 1; // null message to sender // acknowledging real message readyToReceive->Signal(); while (!sendBufferFull) { DEBUG('y', "Waiting for message to receive"); readyToSend->Wait(); } DEBUG('y', "Receiving %c\n", sendBuffer); messageReceived = sendBuffer; sendBufferFull = 0; if (!receiveToken) { // if the sender has already finished DEBUG('y', "Completed receipt of %c\n", messageReceived); sendBusy = 0; receiveBusy = 0; clearToSend->Signal(); clearToReceive->Signal(); } lock->Release(); return(messageReceived); } Exchanger::Exchanger(char *debugname) { name = debugname; lock = new Lock("exchanger lock"); exchangeBusy[0] = 0; exchangeBusy[1] = 0; exchangeBufferFull[0] = 0; exchangeBufferFull[1] = 0; clearToExchange[0] = new Condition("left exchange clear", lock); clearToExchange[1] = new Condition("right exchange clear", lock); readyToExchange[0] = new Condition("left exchange ready", lock); readyToExchange[1] = new Condition("right exchange ready", lock); } Exchanger::~Exchanger() { lock->~Lock(); clearToExchange[0]->~Condition(); clearToExchange[1]->~Condition(); readyToExchange[0]->~Condition(); readyToExchange[1]->~Condition(); } char Exchanger::Exchange(char messageToSend, int direction) { char messageReceived; lock->Acquire(); DEBUG('y', "Trying to exchange %c in direction %i\n", messageToSend, direction); while (exchangeBusy[direction]) { DEBUG('y', "Waiting for clearance to exchange %c in direction %i\n", messageToSend, direction); clearToExchange[direction]->Wait(); } DEBUG('y', "Entering %c in exchangeBuffer, direction %i\n", messageToSend, direction); exchangeBusy[direction] = 1; exchangeBuffer[direction] = messageToSend; exchangeBufferFull[direction] = 1; readyToExchange[direction]->Signal(); while (!exchangeBufferFull[(direction+1)%2]) { DEBUG('y', "Waiting for partner to exchange %c in direction %i\n", messageToSend, direction); readyToExchange[(direction+1)%2]->Wait(); } messageReceived = exchangeBuffer[(direction+1)%2]; exchangeBufferFull[(direction+1)%2] = 0; DEBUG('y', "Exchanged %c in direction %i for %c\n", messageToSend, direction, messageReceived); if (!exchangeBufferFull[direction]) { // if this is the last move of the exchange, enable the next exchange DEBUG('y', "Exchange %c in direction %i for %c complete\n", messageToSend, direction, messageReceived); exchangeBusy[0] = 0; exchangeBusy[1] = 0; clearToExchange[0]->Signal(); clearToExchange[1]->Signal(); } lock->Release(); return(messageReceived); } // End O'D