Top Up Prev Next Bottom Contents Index Search

10.2 Processes


Figure
10-1 shows the class derivation hierarchy for the classes that implement the processes of Kahn process networks. The abstract base class PtThread defines the interface for threads in Ptolemy. The class PosixThread provides an implementation based on the POSIX thread standard. Other implementations using AWESIME [Gru91] or Solaris [Pow91] are possible. The class PNThread is a typedef that determines which implementation is used in the PN domain. Changing the underlying implementation simply requires changing this typedef. The class DataFlowProcess, which is derived from PNThread, implements a dataflow process. The Star object associated with an instance of DataFlowProcess is activated repeatedly, just as a dataflow actor is fired repeatedly to form a process.

10.2.1 The PtThread Class

PtThread is an abstract base class that defines the interface for all thread objects in Ptolemy. Because it has pure virtual methods, it is not possible to create an instance of PtThread. All of the methods are virtual so that objects can be referred to as a generic PtThread, but with the correct implementation-specific functionality.

The class PtThread has three public methods.

virtual void initialize() = 0;
This method initializes the thread and causes it to begin execution.
virtual void runAll();
This method causes all threads to begin (or continue) execution.
virtual void terminate() = 0;
This method causes execution of the thread to terminate.
The class PtThread has one protected method.

virtual void run() = 0;
This method defines the functionality of the thread. It is invoked when the thread begins execution.

10.2.2 The PosixThread Class

The class PosixThread provides an implementation for the interface defined by PtThread. It does not implement the pure virtual method run, so it is not possible to create an instance of PosixThread. This class adds one protected method, and one protected data member to those already defined in PtThread.

static void* runThis(PosixThread*);
This static method invokes the run method of the referenced thread. This provides a C interface that can be used by the POSIX thread library.
pthread_t thread;
A handle for the POSIX thread associated with the PosixThread object.
pthread_attr_t attributes;
A handle for the attributes associated with the POSIX thread.
int detach;
A flag to set the detached state of the POSIX thread.
The initialize method shown below initializes attributes, then creates a thread. The thread is created in a non-detached state, which makes it possible to later synchronize with the thread as it terminates. The controlling thread (usually the main thread) invokes the terminate method of a thread and waits for it to terminate. The priority and scheduling policy for the thread are inherited from the thread that creates it, usually the main thread. A function pointer to the runThis method and the this pointer, which points to the current PosixThread object, are passed as arguments to the pthread_create function. This creates a thread that executes runThis, and passes this as an argument to runThis. Thus, the run method of the PosixThread object is the main function of the thread that is created. The runThis method is required because it would not be good practice to pass a function pointer to the run method as an argument to pthread_create. Although the run method has an implicit this pointer argument by virtue of the fact that it is a class method, this is really an implementation detail of the C++ compiler. By using the runThis method, we make the pointer argument explicit and avoid any dependencies on a particular compiler implementation.


void PosixThread::initialize()
{

// Initialize attributes.
pthread_attr_init(&attributes);

// Detached threads free up their resources as soon
// as they exit; non-detached threads can be joined.
detach = 0;
pthread_attr_setdetachstate(&attributes, &detach);

// New threads inherit their priority and scheduling policy
// from the current thread.
pthread_attr_setinheritsched(&attributes,
PTHREAD_INHERIT_SCHED);

// Set the stack size to something reasonably large. (32K)
pthread_attr_setstacksize(&attributes, 0x8000);

// Create a thread.
pthread_create(&thread, &attributes,
(pthread_func_t)runThis, this);
// Discard temporary attribute object.
pthread_attr_destroy(&attributes);
}
The runAll method, which is shown below, allows all threads to run by lowering the priority of the main thread. If execution of the threads ever stops, control returns to the main thread and its priority is raised again to prevent other threads from continuing.


// Start or continue the running of all threads.
void PosixThread::runAll()
{
// Lower the priority to let other threads run. When control
// returns, restore the priority of this thread to prevent
// others from running.

pthread_attr_t attributes;
pthread_attr_init(&attributes);
pthread_getschedattr(mainThread, &attributes);

pthread_attr_setprio(&attributes, minPriority);
pthread_setschedattr(mainThread, attributes);

pthread_attr_setprio(&attributes, maxPriority);
pthread_setschedattr(mainThread, attributes);

pthread_attr_destroy(&attributes);
}
The terminate method shown below causes the thread to terminate before deleting the PosixThread object. First it requests that the thread associated with the PosixThread object terminate, using the pthread_cancel function. Then the current thread is suspended by pthread_join to give the cancelled thread an opportunity to terminate. Once termination of that thread is complete, the current thread resumes and deallocates resources used by the terminated thread by calling pthread_detach. Thus one thread can cause another to terminate by invoking the terminate method of that thread.


void PosixThread::terminate()
{
// Force the thread to terminate if it has not already done so.
// Is it safe to do this to a thread that has already
// terminated?
pthread_cancel(thread);

// Now wait.
pthread_join(thread, NULL);
pthread_detach(&thread);
}

10.2.3 The DataFlowProcess Class

The class DataFlowProcess is derived from PosixThread. It implements the map higher-order function (see the PN Domain chapter in the User's Manual). A DataFlowStar is associated with each DataFlowProcess object.

DataFlowStar& star;
This protected data member refers to the dataflow star associated with the DataFlowProcess object.
The constructor, shown below, initializes the star member to establish the association between the thread and the star.


DataFlowProcess(DataFlowStar& s)
: star(s) {}
The run method, shown below, is defined to repeatedly invoke the run method of the star associated with the thread, just as the map function forms a process from repeated firings of a dataflow actor. Some dataflow stars in the BDF domain can operate with static scheduling or dynamic, run-time scheduling. Under static scheduling, a BDF star assumes that tokens are available on control inputs and appropriate data inputs. This requires that the scheduler be aware of the values of control tokens and the data ports that depend on these values. Because our scheduler has no such special knowledge, these stars must be properly configured for dynamic, multi-threaded execution in the PN domain. Stars in the BDF domain that have been configured for dynamic execution, and stars in the DDF domain dynamically inform the scheduler of data-dependent firing rules by designating a particular input PortHole with the waitPort method. Data must be retrieved from the designated input before invoking the star's run method. The star's run method is invoked repeatedly, until it indicates an error by returning FALSE.


void DataFlowProcess::run()
{
// Configure the star for dynamic execution.
star.setDynamicExecution(TRUE);

// Fire the Star ad infinitum.
do
{
if (star.waitPort()) star.waitPort()->receiveData();
} while(star.run());
}


Top Up Prev Next Bottom Contents Index Search

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