NachOS/code/threads/synch.cc

164 lines
4.5 KiB
C++

// 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"
//----------------------------------------------------------------------
// 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 (const 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;
queue = NULL;
value = -1;
}
//----------------------------------------------------------------------
// 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
ASSERT(value >= 0);
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);
ASSERT(value >= 0);
thread = (Thread *) queue->Remove ();
if (thread != NULL) // make thread ready, consuming the V immediately
scheduler->ReadyToRun (thread);
value++;
(void) interrupt->SetLevel (oldLevel);
}
// Dummy functions -- so we can compile our later assignments
// Note -- without a correct implementation of Condition::Wait(),
// the test case in the network assignment won't work!
Lock::Lock (const char *debugName)
{
(void) debugName;
/* TODO */
ASSERT(FALSE);
}
Lock::~Lock ()
{
}
void
Lock::Acquire ()
{
/* TODO */
ASSERT(FALSE);
}
void
Lock::Release ()
{
/* TODO */
ASSERT(FALSE);
}
Condition::Condition (const char *debugName)
{
(void) debugName;
/* TODO */
ASSERT(FALSE);
}
Condition::~Condition ()
{
}
void
Condition::Wait (Lock * conditionLock)
{
(void) conditionLock;
/* TODO */
ASSERT (FALSE);
}
void
Condition::Signal (Lock * conditionLock)
{
(void) conditionLock;
/* TODO */
ASSERT(FALSE);
}
void
Condition::Broadcast (Lock * conditionLock)
{
(void) conditionLock;
/* TODO */
ASSERT(FALSE);
}