Top Up Prev Next Bottom Contents Index Search

10.3 Communication Channels


Figure
10-2 shows the class derivation hierarchy for the classes that implement the communication channels of Kahn process networks. The classes that implement the communication channels provide the synchronization necessary to enforce the blocking read semantics of Kahn process networks. The classes PtGate, PosixMonitor and CriticalSection provide a mutual exclusion mechanism. The classes PtCondition and PosixCondition provide a synchronization mechanism. The class PNGeodesic uses these classes to implement a communication channel that enforces the blocking read operations of Kahn process networks and the blocking write operations required for bounded scheduling.

The abstract base class PtGate defines the interface for mutual exclusion objects in Ptolemy. The class PosixMonitor provides an implementation of PtGate based on the POSIX thread standard. Other implementations are possible. The class PNMonitor is a typedef that determines which implementation is used in the PN domain. Changing the underlying implementation simply requires changing this typedef.

The abstract base class PtCondition defines the interface for condition variables in Ptolemy. The class PosixCondition provides an implementation based on the POSIX thread standard. Other implementations are possible. The class PNCondition is a typedef that determines which implementation is used in the PN domain. Changing the underlying implementation simply requires changing this typedef.

The class CriticalSection provides a convenient method for manipulating PtGate objects, preventing some common programming errors. The class PNGeodesic uses all of these classes to implement a communication channel.

10.3.1 PtGate

A PtGate can be locked and unlocked, but only one thread can hold the lock. Thus if a thread attempts to lock a PtGate that is already locked by another thread, it is suspended until the lock is released.

virtual void lock() = 0;
This protected method locks the PtGate object for exclusive use by one thread.
virtual void unlock() = 0;
This protected method releases the lock on the PtGate object.

10.3.2 PosixMonitor

The class PosixMonitor provides an implementation for the interface defined by PtGate. It has a single protected data member.

pthread_mutex_t thread;
A handle for the POSIX monitor associated with the PosixMonitor object.
The implementations of the lock and unlock methods are shown below.


void PosixMonitor::lock()
{
pthread_mutex_lock(&mutex);
}

void PosixMonitor::unlock()
{
pthread_mutex_unlock(&mutex);
}

10.3.3 CriticalSection

The class CriticalSection provides a convenient mechanism for locking and unlocking PtGate objects. Its constructor, shown below, locks the gate. Its destructor, also shown below, unlocks the gate. To protect a section of code, simply create a new scope and declare an instance of CriticalSection. The PtGate is locked as soon as the CriticalSection is constructed. When execution of the code exits scope, the CriticalSection destructor is automatically invoked, unlocking the PtGate and preventing errors caused by forgetting to unlock it. Examples of this usage are shown in Section 10.3.6. Because only one thread can hold the lock on a PtGate, only one section of code guarded in this way can be active at a given time.


CriticalSection(PtGate* g) : mutex(g)
{
if (mutex) mutex->lock();
}

~CriticalSection()
{
if (mutex) mutex->unlock();
}

10.3.4 PtCondition

The class PtCondition defines the interface for condition variables in Ptolemy. A PtCondition provides synchronization through the wait and notify methods. A condition variable can be used only when executing code within a critical section (i.e., when a PtGate is locked).

PtGate& mon;
This data member refers to the gate associated with the PtCondition object.
virtual void wait() = 0;
This method suspends execution of the current thread until notification is received. The associated gate is unlocked before execution is suspended. Once notification is received, the lock on the gate is automatically reacquired before execution resumes.
virtual void notify() = 0;
This method sends notification to one waiting thread. If multiple threads are waiting for notification, only one is activated.
virtual void notifyAll() = 0;
This method sends notification to all waiting threads. If multiple threads are waiting for notification, all of them are activated. Once activated, all of the threads attempt to reacquire the lock on the gate, but only one of them succeeds. The others are suspended again until they can acquire the lock on the gate.

10.3.5 PosixCondition

The class PosixCondition provides an implementation for the interface defined by PtCondition. The implementations of the wait, notify and notifyAll methods are shown below.


void PosixCondition::wait()
{
// Guarantee that the mutex will not remain locked
// by a cancelled thread.
pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock,
&mutex);

pthread_cond_wait(&condition, &mutex);

// Remove cleanup handler, but do not execute.
pthread_cleanup_pop(FALSE);
}

void PosixCondition::notify()
{
pthread_cond_signal(&condition);
}

void PosixCondition::notifyAll()
{
pthread_cond_broadcast(&condition);
}

10.3.6 PNGeodesic

The class PNGeodesic, which is derived from the class Geodesic defined in the Ptolemy kernel, implements the communication channels for the PN domain. In conjunction with the PtGate member provided in the base class Geodesic, two condition variables provide the necessary synchronization for blocking read and blocking write operations.

PtCondition* notEmpty;
This data member points to a condition variable used for blocking read operations when the channel is empty.
PtCondition* notFull;
This data member points to a condition variable used for blocking write operations when the channel is full.
int cap;
This data member represents the capacity of the communication channel and determines when it is full.
static int numFull;
This static data member records the number of full geodesics in the system.
The slowGet method, shown in below, implements the get operation for communication channels. The entire method executes within a critical section to ensure consistency of the object's data members. If the buffer is empty, then the thread that invoked slowGet is suspended until notification is received on notEmpty. Data is retrieved from the buffer, and if it is not full notification is sent on notFull to any other thread that may have been waiting.


Particle* PNGeodesic::slowGet()
{
// Avoid entering the gate more than once.
CriticalSection region(gate);
while (sz < 1 && notEmpty) notEmpty->wait();
sz--;
Particle* p = pstack.get();
if (sz < cap && notFull) notFull->notifyAll();
return p;
}
The slowPut method, shown below, implements the put operation for communication channels. The entire method executes within a critical section to ensure consistency of the object's data members. If the buffer is full, then the thread that invoked slowPut is suspended until notification is received on notFull. Data is placed in the buffer, and notification is sent on notEmpty to any other thread that may have been waiting.


// Block when full.
// Notify when not empty.
void PNGeodesic::slowPut(Particle* p)
{
// Avoid entering the gate more than once.
CriticalSection region(gate);
if (sz >= cap && notFull)
{
{
CriticalSection region(fullGate);
numFull++;
}
while (sz >= cap && notFull) notFull->wait();
{
CriticalSection region(fullGate);
numFull--;
}
}
pstack.putTail(p); sz++;
if (notEmpty) notEmpty->notifyAll();
}
The setCapacity method, shown below, is used to adjust the capacity limit of communication channels. If the capacity is increased so that a channel is no longer full, notification is sent on notFull to any thread that may have been waiting.


void PNGeodesic::setCapacity(int c)
{
CriticalSection region(gate);
cap = c;
if (sz < cap && notFull) notFull->notifyAll();
}


Top Up Prev Next Bottom Contents Index Search

Copyright © 1990-1997, University of California. All rights reserved.