#!/bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #!/bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # . # This archive created: Thu Sep 8 10:55:58 1988 export PATH; PATH=/bin:$PATH echo shar: extracting "'README'" '(1260 characters)' if test -f 'README' then echo shar: over-writing existing file "'README'" fi sed 's/^X//' << \SHAR_EOF > 'README' XThis is an implementation of the shared-memory constructs from ANL Parmacs X(Lusk, Overbeek, et.al.) in C++. It illustrates how C++ can be used to build Xparallel programming constructs, and numerous advantages over the ANL M4 Ximplementation. These advantages include extensibility, strong type Xchecking, ease of readability and use, and use of an object-oriented Xlanguage. X XThe code includes the classes and header files for all the shared-memory Xparmacs constructs (eg, BasicLock, Monitor, Barrier, Getsub, Askfor), and Xseveral useful derived classes (LeakLast, LeakOne, SimpleQ, GeneralQ). There Xare also a set of simple examples. The code should compile with AT&T C++ Xv1.2.1, and uses the Sequent parallel programming library (-lpps). X XNo documentation is included yet, although the code is reasonably documented Xand is (hopefully) sufficient. The author (Bob Beck of Sequent) has a paper Xdescribing this work submitted for publication; when this is available, a Xreference will be included with the code. X XDirect questions, comments, problems, additions, etc, to: X X Bob Beck X Principal Engineer X Sequent Computer Systems X 15450 SW Koll Parkway X Beaverton, Oregon 97006-6063 X ...{anlams,tektronix,ogcvax}!sequent!rbk X (503)626-5700 SHAR_EOF if test 1260 -ne "`wc -c 'README'`" then echo shar: error transmitting "'README'" '(should have been 1260 characters)' fi echo shar: extracting "'Makefile'" '(495 characters)' if test -f 'Makefile' then echo shar: over-writing existing file "'Makefile'" fi sed 's/^X//' << \SHAR_EOF > 'Makefile' XP=& X XDEST= /usr/local/c++/parmacs X XCCFLAGS=-O -Y X X.SUFFIXES: .C X X.C.o:; CC ${CCFLAGS} -c $? X XLIB= libparmacs.a XLIBOBJS=parmacs.o shnew.o shdelete.o slist.o main.o pinit.o pfinish.o X Xall: ${LIB} X X${LIB}:$P ${LIBOBJS} X ar rcv $@ ${LIBOBJS}; \ X ranlib $@ X Xinstall: all PCC X -mkdir ${DEST} X -rm -f ${DEST}/* X cp ${LIB} *.h ${DEST} X sed "s;DEST;${DEST};" < PCC > ${DEST}/PCC X chmod +x ${DEST}/PCC X ranlib ${DEST}/${LIB} X Xdoc: X /usr/imagen/bin/itpf -x parmacs.ms X Xclean: X rm -f *.c *.o *.a *.out core SHAR_EOF if test 495 -ne "`wc -c 'Makefile'`" then echo shar: error transmitting "'Makefile'" '(should have been 495 characters)' fi echo shar: extracting "'PCC'" '(185 characters)' if test -f 'PCC' then echo shar: over-writing existing file "'PCC'" fi sed 's/^X//' << \SHAR_EOF > 'PCC' X#! /bin/sh X# PCC -- Compile "parallel" C++ code that uses the parmacs classes. X# -Y ==> make all static data shared. X XPARDIR=DEST Xexec CC -Y -I${PARDIR} $* ${PARDIR}/libparmacs.a -lpps SHAR_EOF if test 185 -ne "`wc -c 'PCC'`" then echo shar: error transmitting "'PCC'" '(should have been 185 characters)' fi chmod +x 'PCC' if test ! -d 'examples' then echo shar: creating directory "'examples'" mkdir 'examples' fi echo shar: entering directory "'examples'" cd 'examples' echo shar: extracting "'af.C'" '(1140 characters)' if test -f 'af.C' then echo shar: over-writing existing file "'af.C'" fi sed 's/^X//' << \SHAR_EOF > 'af.C' X/* X * Simple example of Askfor monitor with derived class. Monitor manages X * queued structure instances, each representing a unit of work. X */ X X#include X#include X Xstruct elt { X elt* e_next; X int e_work; X}; X Xclass work : public Askfor { X elt* queue; // queue of work X int getprob(void*& prob) { // problem get'er X elt* work; X if (queue != NULL) { // Non-empty ==> X work = queue; // de-queue work X queue = work->e_next; X prob = work; // return pointer X return 1; // and indicate got one X } else X return 0; // no work available X } Xpublic: X work() { queue = NULL; } X void putprob(elt* prob) { // add work. X enter(); // lock the monitor. X prob->e_next = queue; // queue the... X queue = prob; // ...new work. X signal(); // tell'em it's there. X } X}; X Xvoid Xpinit() X{ X set_numprocs(1); // force for the example X} X Xvoid Xpmain() X{ X int i; X elt* prob; X work queue; X int N = 20; X X for (i = 0; i < N; i++) { X prob = new elt; X prob->e_work = i; X queue.putprob(prob); X } X X while (queue.getwork(prob) == 0) { X cout << prob->e_work << " "; X delete prob; X } X X cout << "\n"; X} SHAR_EOF if test 1140 -ne "`wc -c 'af.C'`" then echo shar: error transmitting "'af.C'" '(should have been 1140 characters)' fi echo shar: extracting "'gQ.C'" '(617 characters)' if test -f 'gQ.C' then echo shar: over-writing existing file "'gQ.C'" fi sed 's/^X//' << \SHAR_EOF > 'gQ.C' X/* X * Demonstrate use of GeneralQ monitor. X */ X X#include X#include X Xstruct coord { X int x, y; X coord(int X, int Y) { x = X; y = Y; } X}; X Xvoid Xpinit() X{ X set_numprocs(1); // force for the example X} X Xvoid Xpmain(int argc, char **argv) X{ X int i; X coord* prob; X GeneralQ queue; X int N = 100; X X if (argc > 1) X N = atoi(argv[1]); X X queue.set_cache(2*N); X X for (i = 0; i < N; i++) { X // queue.append(new coord(i, i*i)); X queue.insert(new coord(i, i*i)); X } X X while (queue.getwork(prob) == 0) { X cout << prob->x << " " << prob->y << "\n"; X if (prob->x == 20) X queue.probend(); X delete prob; X } X} SHAR_EOF if test 617 -ne "`wc -c 'gQ.C'`" then echo shar: error transmitting "'gQ.C'" '(should have been 617 characters)' fi echo shar: extracting "'gs.C'" '(262 characters)' if test -f 'gs.C' then echo shar: over-writing existing file "'gs.C'" fi sed 's/^X//' << \SHAR_EOF > 'gs.C' X/* X * Simple example of Getsub monitor. Can run in parallel. X */ X X#include X#include X Xint N = 20; XGetsub Index(1, N); X Xvoid Xpmain() X{ X int i; X X while (i = Index()) { X cout << i << "\n"; X } X X while (i = Index()) { X cout << i << " "; X } X} SHAR_EOF if test 262 -ne "`wc -c 'gs.C'`" then echo shar: error transmitting "'gs.C'" '(should have been 262 characters)' fi echo shar: extracting "'gsaf.C'" '(712 characters)' if test -f 'gsaf.C' then echo shar: over-writing existing file "'gsaf.C'" fi sed 's/^X//' << \SHAR_EOF > 'gsaf.C' X/* X * Getsub implemented as an Askfor monitor, to demonstrate generality of Askfor. X */ X X#include X#include X Xclass getsub : Askfor { X int min, max, sub; X int getprob(void*& prob) { X if (sub <= max) { X *(int*)prob = sub++; X return 1; X } else X return 0; X } X void reset() { sub = min; } Xpublic: X int next() { X int sub; X if (getwork(&sub) == 0) X return sub; X else X return min-1; X } X getsub(int Min, int Max, int Nproc = 0) : (Nproc) { X sub = min = Min; X max = Max; X } X int operator()() { return next(); } X}; X Xint N = 20; Xgetsub Index(1, N); X Xvoid Xpmain() X{ X int i; X X while (i = Index()) { X cout << i << "\n"; X } X X while (i = Index()) { X cout << i << " "; X } X} SHAR_EOF if test 712 -ne "`wc -c 'gsaf.C'`" then echo shar: error transmitting "'gsaf.C'" '(should have been 712 characters)' fi echo shar: extracting "'sQ.C'" '(635 characters)' if test -f 'sQ.C' then echo shar: over-writing existing file "'sQ.C'" fi sed 's/^X//' << \SHAR_EOF > 'sQ.C' X/* X * Demonstrate use of SimpleQ monitor. X */ X X#include X#include X Xstruct coord : SimpleLink { // must be derived from SimpleLink X int x, y; X coord(int X, int Y) { x = X; y = Y; } X}; X Xvoid Xpinit() X{ X set_numprocs(1); // force for the example X} X Xvoid Xpmain(int argc, char **argv) X{ X int i; X coord* prob; X SimpleQ queue; X int N = 100; X X if (argc > 1) X N = atoi(argv[1]); X X for (i = 0; i < N; i++) X // queue.append(new coord(i, i*i)); X queue.insert(new coord(i, i*i)); X X while (queue.getwork(prob) == 0) { X cout << prob->x << " " << prob->y << "\n"; X if (prob->x == 20) X queue.probend(); X delete prob; X } X} SHAR_EOF if test 635 -ne "`wc -c 'sQ.C'`" then echo shar: error transmitting "'sQ.C'" '(should have been 635 characters)' fi echo shar: extracting "'prime.C'" '(1117 characters)' if test -f 'prime.C' then echo shar: over-writing existing file "'prime.C'" fi sed 's/^X//' << \SHAR_EOF > 'prime.C' X/* X * Old dumb primes program again, this time in C++ with parmacs classes. X */ X X#include X#include X Xvoid Xusage(char* prog) X{ X cerr << "Usage: " << prog << " numprimes\n"; X exit(1); X} X Xint maxprime; // last interesting number Xchar* array; // array filled to indicate prime-ness XGetsub* candidates; // index iterator for prime candidates X Xvoid pinit(int argc, char **argv) X{ X if (argc < 2) X usage(argv[0]); X maxprime = atoi(argv[1]); X array = new char[maxprime+1]; X candidates = new Getsub(1,maxprime); X} X X/* X * Illustrate auto-reset of "Getsub" monitor by initializing in parallel X * first, then determining the primes in parallel. X */ X Xvoid pmain() X{ X register int i; X int isprime(int); X X while (i = candidates->next()) X array[i] = ' '; X X while (i = candidates->next()) X if (isprime(i)) X array[i] = '*'; X X while (i = candidates->next()) X if (array[i] == '*') X cout << i << "\n"; X} X Xisprime(int number) X{ X extern double sqrt(double); X X if (number < 2) X return 0; X X int max = (int) sqrt(number) + 1; X for (int i = 2; i < max; i++) X if (number % i == 0) X return 0; X return 1; X} SHAR_EOF if test 1117 -ne "`wc -c 'prime.C'`" then echo shar: error transmitting "'prime.C'" '(should have been 1117 characters)' fi echo shar: extracting "'Makefile'" '(615 characters)' if test -f 'Makefile' then echo shar: over-writing existing file "'Makefile'" fi sed 's/^X//' << \SHAR_EOF > 'Makefile' XP=& X XINCL= -I.. XCCFLAGS=-O -Y ${INCL} XHDRS= ../parmacs.h ../slist.h ../workQ.h X X.SUFFIXES: .C X X.C.o:; CC ${CCFLAGS} -c $< X XLIB= ../libparmacs.a X XTESTS= gs af gsaf gQ sQ mQ prime ll lo XTESTOBJS= gs.o af.o gsaf.o gQ.o sQ.o mQ.o prime.o ll.o lo.o X Xall: testobjs testprogs X Xtestobjs:$P ${TESTOBJS} X Xtestprogs:$P ${TESTS} X X${TESTS}: X CC -o $@ $@.o ${LIB} -lpps -lm X Xclean: X rm -f ${TESTS} *.c *.o *.a a.out *.out *.dis core X Xgs: gs.o ${LIB} Xaf: af.o ${LIB} Xgsaf: gsaf.o ${LIB} XgQ: gQ.o ${LIB} XsQ: sQ.o ${LIB} XmQ: mQ.o ${LIB} Xprime: prime.o ${LIB} Xll: ll.o ${LIB} Xlo: lo.o ${LIB} X X${TESTOBJS}: ${HDRS} SHAR_EOF if test 615 -ne "`wc -c 'Makefile'`" then echo shar: error transmitting "'Makefile'" '(should have been 615 characters)' fi echo shar: extracting "'mQ.C'" '(468 characters)' if test -f 'mQ.C' then echo shar: over-writing existing file "'mQ.C'" fi sed 's/^X//' << \SHAR_EOF > 'mQ.C' X/* X * Simple example of Getsub loop creating work that's being consumed by X * a SimpleQ. X */ X X#include X#include X Xconst int N = 100; X XSimpleQ queue; XGetsub Index(1,N); X Xstruct coord : SimpleLink { X int x, y; X coord(int X, int Y) { x = X; y = Y; } X}; X Xvoid Xpmain() X{ X int i; X coord* prob; X X while (i = Index()) X queue.insert(new coord(i, i*i)); X X while (queue.getwork(prob) == 0) { X cout << prob->x << " " << prob->y << "\n"; X delete prob; X } X} SHAR_EOF if test 468 -ne "`wc -c 'mQ.C'`" then echo shar: error transmitting "'mQ.C'" '(should have been 468 characters)' fi echo shar: extracting "'ll.C'" '(390 characters)' if test -f 'll.C' then echo shar: over-writing existing file "'ll.C'" fi sed 's/^X//' << \SHAR_EOF > 'll.C' X/* X * Example of LeakLast barrier. X */ X X#include X#include X XBasicLock l; XLeakLast last; X Xint getpid(); X Xvoid pmain() X{ X l.lock(); X cout << getpid() << "\n"; X l.unlock(); X X if (last.enter()) X cout << "last in was " << getpid() << "\n"; X if (last.enter()) X cout << "last in was " << getpid() << "\n"; X if (last.enter()) X cout << "last in was " << getpid() << "\n"; X} SHAR_EOF if test 390 -ne "`wc -c 'll.C'`" then echo shar: error transmitting "'ll.C'" '(should have been 390 characters)' fi echo shar: extracting "'lo.C'" '(439 characters)' if test -f 'lo.C' then echo shar: over-writing existing file "'lo.C'" fi sed 's/^X//' << \SHAR_EOF > 'lo.C' X/* X * Example of LeakOne barrier. X */ X X#include X#include X XBasicLock l; XLeakOne one; X Xint getpid(); X Xvoid pmain() X{ X l.lock(); X cout << getpid() << "\n"; X l.unlock(); X X if (one.enter()) { X cout << "winner was " << getpid() << "\n"; X one.exit(); X } X if (one.enter()) { X cout << "winner was " << getpid() << "\n"; X one.exit(); X } X if (one.enter()) { X cout << "winner was " << getpid() << "\n"; X one.exit(); X } X} SHAR_EOF if test 439 -ne "`wc -c 'lo.C'`" then echo shar: error transmitting "'lo.C'" '(should have been 439 characters)' fi echo shar: done with directory "'examples'" cd .. echo shar: extracting "'main.C'" '(737 characters)' if test -f 'main.C' then echo shar: over-writing existing file "'main.C'" fi sed 's/^X//' << \SHAR_EOF > 'main.C' X#ifndef lint Xstatic char rcsid[] = "$Header: main.C 1.3 88/04/04 $"; X#endif X X/* X * main.C X * Main program for parallel-C++ with parmacs classes. X */ X X/* $Log: main.C,v $ X */ X X/* X * main() X * Calls pinit() while still serial (to allow for any initializations), X * then m_fork()'s pmain() (ie, starts parallel execution). X * When done, kill slaves and call pfinish() (again, serial). X */ X Xtypedef void (*VF)(...); X Xvoid m_fork(VF, ...); Xvoid m_kill_procs(); X Xvoid pinit(int, char**); Xvoid pmain(int, char**); Xvoid pfinish(); X Xmain(int argc, char** argv) X{ X pinit(argc, argv); // serial initializations X m_fork(pmain, argc, argv); // parallel main program X m_kill_procs(); // kill slave processes X pfinish(); // serial finish up X} SHAR_EOF if test 737 -ne "`wc -c 'main.C'`" then echo shar: error transmitting "'main.C'" '(should have been 737 characters)' fi echo shar: extracting "'parmacs.C'" '(5162 characters)' if test -f 'parmacs.C' then echo shar: over-writing existing file "'parmacs.C'" fi sed 's/^X//' << \SHAR_EOF > 'parmacs.C' X#ifndef lint Xstatic char rcsid[] = "$Header: parmacs.C 1.8 88/04/04 $"; X#endif X X/* X * parmacs.C X * Out of line implementations of parmacs classes. See parmacs.h X * for class definitions. X * X * Much of the parmacs classes are inline's for efficiency. X */ X X/* $Log: parmacs.C,v $ X */ X X#include "parmacs.h" X X/* X * set_numprocs() X * Arrange for given number processors. Returns -1 if can't satisfy. X * X * Typically called in pinit() to arrange # processors before creating X * monitors. If not called, the environment variable PARALLEL is used, X * and if that isn't defined, a system default. Once number processors X * is determined, subsequent calls to set_numprocs() fail. X */ X Xextern int m_numprocs; // -lpps stores default here Xextern int m_set_procs(int); // -lpps set # processes Xextern int m_get_numprocs(); // -lpps return # processes X Xset_numprocs(int Nprocs) X{ X if (m_numprocs > 0) // can't change once set. X return -1; X else X return m_set_procs(Nprocs); X} X X/* X * get_numprocs() X * Return number of processors to use. X * Uses a default if none yet declared (see above). X * X * Augments -lpps by importing environment variable PARALLEL. X * X * 1st call sets number of processes to use to PARALLEL environment variable or X * -lpps default. Use set_numprocs() to set a different number of processors X * (but must call before call get_numprocs()). X */ X Xint Xget_numprocs() X{ X extern char* getenv(char*); X extern int atoi(char*); X X if (m_numprocs < 0) { X char* parallel; X if (parallel = getenv("PARALLEL")) X m_set_procs(atoi(parallel)); // should check error X else X m_set_procs(m_get_numprocs()); // shouldn't error X } X return m_numprocs; X} X X/* X * BasicLock is completely defined with inlines. X */ X X/* X * Basic monitor, including "wait" and "signal". X */ X XMonitor::Monitor() X{ X qcount = 0; // noone queued yet X qwait.lock(); // waiting lock starts out locked X} X X/* X * Simple barrier. X * Block all callers until all have enterred. X */ X XBarrier::Barrier(int Nprocs) // initialize # processes X{ X nprocs = (Nprocs ? Nprocs : get_numprocs()); X} X X/* X * Subscript server monitor: X * Return next subscript, atomicly. X */ X XGetsub::Getsub(int Min, int Max, int Nprocs) : (Nprocs) X{ X sub = min = Min; X max = Max; X} X X/* X * Generalized "askfor" monitor: X * Base class for general work queueing and distribution. X */ X XAskfor::Askfor(int Nprocs) : (Nprocs) X{ X done = 0; X} X X/* X * Askfor::getwork() X * Get a unit of work from monitor, return code indicates success. X * X * Return code = X * 0 == got work X * -1 == program done X * 1 == ran out of problems X * > 1 == someone declared problem done X * X * See parmacs.h and the book for more details. X * X * Implementation uses one "done" flag, not 2 as in ANL macros. The done X * flag is encoded to allow fewer tests in "hot path": done == 0 ==> still X * active; done = -1 ==> "program" done; done > 0 ==> "problem" done. X * probend() and progend() functions insure encoding stays valid. X */ X XAskfor::getwork(void*& prob) X{ X /* X * If problem marked "done" wait here for others to arrive (or X * "program" done). Otherwise, loop looking for work until X * "done" is set. X */ X enter(); X if (done > 0) { X if (!lastin()) X wait(); X } else while (!done) { X /* X * Try to get a problem (unit of work). If got one, return. X * getprob() is defined in a derived class, fills out prob or X * what prob points at (depends on derived class definition). X */ X if (getprob(prob)) { // true if got work, filled out `prob' X signal(); // try next, if appropriate X return 0; // ==> got a problem X } X /* X * Didn't get a problem. If last one to arrive, declare X * problem done (ran out of work to do). Otherwise, wait for X * new problem(s) or problem/program done. X */ X if (lastin()) X done = 1; X else X wait(); X } X /* X * Land here if problem or program declared done. X * X * While blocked in wait() above, program can be declared "done". But X * once "signal" loop starts, no process can re-enter the monitor X * (since signal() gives it to a waiter rather then exit monitor). Thus X * if done > 0 here (problem-done) it cannot be set < 0 (program-done). X * X * Note that once declare program-done (done < 0), done is never reset. X */ X if (done < 0) { // "program" done X signal(); // let others see it X return -1; // ==> program done X } X /* X * Problem was declared done. The wait()'s above form a barrier; last X * in the barrier starts the exodus. Last out of the barrier resets X * the problem logic and problem-done flag; must be last out to X * insure all see the problem-done code before it's reset. X */ X int return_code = done; X if (lastout()) { // last to leave... X reset(); // turns out... X done = 0; // the lights. X } X signal(); X return return_code; X} X Xvoid XAskfor::probend(int code) // declare sub-problem done X{ X enter(); X if (done == 0 && code > 0) done = code; X exit(); X} X Xvoid XAskfor::progend() // declare entire program done X{ X enter(); X done = -1; // unconditionally set < 0 ==> program done X signal(); X} X XAskfor::getprob(void*&) // default returns no problem exists X{ X return 0; X} X Xvoid XAskfor::reset() // default logic-reset function (NOP) X{ X /*NOP*/ X} SHAR_EOF if test 5162 -ne "`wc -c 'parmacs.C'`" then echo shar: error transmitting "'parmacs.C'" '(should have been 5162 characters)' fi echo shar: extracting "'parmacs.h'" '(8494 characters)' if test -f 'parmacs.h' then echo shar: over-writing existing file "'parmacs.h'" fi sed 's/^X//' << \SHAR_EOF > 'parmacs.h' X/* X * $Header: parmacs.h 1.10 88/04/04 $ X * X * parmacs.h X * C++ classes defining basic parallel programming constructs. X * X * All locking constructs are spin-lock based (eg, busy-wait). X * X * Flavor of interface/implementation comes from the Argonne Labs "parmacs" X * m4 macros, described in the book "Portable Programs for Parallel X * Processors", Lusk, Overbeek, et.al., Holt, Rinehart, and Winston, Inc, X * ISBN 0-03-014153-2. X */ X X/* $Log: parmacs.h,v $ X */ X X#ifndef PARMACS_H X#define PARMACS_H X X/* X * set_numprocs() X * Arrange for given number processors. Returns -1 if can't satisfy. X * X * Typically called in pinit() to arrange # processors before creating X * monitors. If not called, the environment variable PARALLEL is used, X * and if that isn't defined, a system default. Once number processors X * is determined, subsequent calls to set_numprocs() are ignored. X * X * get_numprocs() X * Return number of processes(ors) in use. X * Uses a default if none yet declared (see above). X */ X Xextern set_numprocs(int Nprocs); Xextern get_numprocs(); X X/* X * Basic Locks -- low-level mutual-exclusion. X * X * Implemented via Sequent Parallel Programming Library spin-locks. X */ X Xtypedef unsigned char slock_t; X Xconst slock_t S_UNLOCKED = 0; Xconst slock_t S_LOCKED = 1; X Xextern void s_lock(slock_t*); // From -lpps Xextern void s_unlock(slock_t*); // From -lpps X Xclass BasicLock { X slock_t mutex; Xpublic: X void lock() { s_lock(&mutex); } X void unlock() { s_unlock(&mutex); } X BasicLock() { mutex = S_UNLOCKED; } X}; X X/* X * Basic monitor, including "wait" and "signal". X * X * signal() passes ownership of monitor to a wait()'ing process. X */ X Xclass Monitor { X BasicLock mutex; X BasicLock qwait; X int qcount; Xpublic: X Monitor(); X void enter() { mutex.lock(); } X void exit() { mutex.unlock(); } X void wait() { ++qcount; exit(); qwait.lock(); --qcount; } X void signal() { if (qcount > 0) qwait.unlock(); else exit(); } X int qlen() { return qcount; } X}; X X/* X * Simple barrier. X * Block all callers until all have enterred. X * X * Initialized with number of processes involved. Automatically resets. X * X * Protected interface lastin() allows derived class to have barrier function X * with atomic reset by last process to enter. lastin() returns true if caller X * is last to enter the barrier, else false. Expected use in derived class is: X * X * if (lastin()) { X * reset derived class; X * signal(); X * } else X * wait(); X */ X Xclass Barrier : public Monitor { // portable one-at-a-time barrier X int nprocs; // # participating processes Xprotected: X int lastin(); // caller would be last to enter?? X int lastout(); // caller would be last to leave?? Xpublic: X Barrier(int Nprocs = 0); X void wait(); // "block" at barrier, wait for others X}; X Xinline int Barrier::lastin() { return qlen() == nprocs-1; } Xinline int Barrier::lastout() { return qlen() == 0; } X Xinline void XBarrier::wait() X{ X enter(); // lock up monitor X if (!lastin()) // !last in ==> X Monitor::wait(); // wait for last X signal(); // start'em (or let next) going X} X X/* X * LeakLast X * Barrier that leaks last caller. X * X * Last one to "enter" the barrier starts other leaving and returns indication X * it is the last in. All are concurrent once last one has entered. X * X * Typical use is: X * LeakLast onlyone; X * if (onlyone.enter()) X * // only one does this X * // all do this X * if (onlyone.enter()) X * // only one does this (maybe different one than first time) X * // all do this X */ X Xclass LeakLast : Barrier { Xpublic: X LeakLast(int Nprocs = 0) : (Nprocs) {} X int enter(); X}; X Xinline int XLeakLast::enter() X{ X Monitor::enter(); // lock up monitor X if (lastin()) { // last one in ... X Monitor::signal(); // let others go X return 1; // and let caller know X } else { // otherwise ... X Monitor::wait(); // wait for last one X Monitor::signal(); // let next go. X return 0; // wasn't last one in X } X} X X/* X * LeakOne X * Barrier that leaks one caller, others wait for it to say it's finished. X * X * Typical use is: X * LeakOne onlyone; X * if (onlyone.enter()) { X * // only one does this X * onlyone.exit(); X * } X * // all do this X * X * This is typically used for initialization code that must only be done X * once, before other processes can continue; for example, allocation X * of new dynamic variables. X * X * Implementation has the first to enter do the work while others enter X * the barrier queue (better parallelism). X */ X Xclass LeakOne : Barrier { X int firstin; // flag telling if first in Xpublic: X LeakOne(int Nprocs = 0) : (Nprocs) { firstin = 1; } X int enter(); X void exit(); X}; X Xinline int XLeakOne::enter() X{ X Monitor::enter(); // lock up monitor X if (firstin) { // first one in ... X firstin = 0; // marks it as so X Monitor::exit(); // lets others queue X return 1; // and does the work. X } else { // otherwise ... X if (lastin()) // last in... X firstin = 1; // resets flag. X else // otherwise... X Monitor::wait(); // wait for last. X Monitor::signal(); // let next go. X return 0; // wasn't last one in X } X} X Xinline void XLeakOne::exit() X{ X Monitor::enter(); // lock up monitor X if (lastin()) { // last one in ... X firstin = 1; // resets flag X } else { // otherwise ... X Monitor::wait(); // wait for last one X } X Monitor::signal(); // let next go. X} X X/* X * Subscript server monitor: X * Return next subscript, atomicly. X * X * Each call to next() returns next subscript, atomicly. X * When "done", all callers block at a barrier, and return min-1 after X * all have arrived. Automatically resets. X * X * Operator "()" interface to next() is also provided; eg, X * Getsub index(1,N,Nprocs); X * while (i = index()) { ... } X */ X Xclass Getsub : Barrier { X int min, max; // min and max subscripts X int sub; // next subscript to return X void wait(); // avoid confusion with Barrier::wait Xpublic: X Getsub(int Min, int Max, int Nprocs = 0); X int next(); // return next subscript, or min-1 X int operator()(); // nice interface to next() X}; X Xinline void Getsub::wait() { Monitor::wait(); } X Xinline int XGetsub::next() X{ X int val; X X enter(); // lock up monitor X if (sub <= max) { // have more left... X val = sub++; // return next. X exit(); // done. X } else { // no more left... X val = min - 1; // return min-1. X if (lastin()) // last one in... X sub = min; // resets subscript X else // !last one in... X wait(); // ==> wait for last to arrive X signal(); // start'em (or let next) going X } X return val; X} X Xinline int Getsub::operator()() { return next(); } X X/* X * Generalized "askfor" monitor: X * Base class for general work queueing and distribution. X * X * Primary use is to call getwork() with a single argument, a pointer to a some X * structure type; getwork() will dequeue one of these from the monitor and X * assign its address to the pointer argument. getwork() returns 0 if a X * problem (piece of work) has been allocated, -1 for "program done", or > 0 X * for "sub-problem" done (value can be passed to probdone()). X * X * Derived-class provides "get-problem" logic and "reset" logic (if any): X * X * getprob(prob) X * If there is work to do, dequeue a piece of work, make `prob' X * point at it, and return true. Else, return false. X * X * reset() X * Is called between sub-problems, when all processes have blocked X * at a barrier. reset() should do whatever necessary to set up X * for next sub-problem. This is optional. X * X * These are called with the monitor already locked; ie, don't call enter(), X * as this will deadlock. X * X * Other utility functions available to derived class: X * X * enter() X * Lock the monitor. X * X * exit() X * Unlock the monitor. X * X * Semantics are very close to the ANL ASKFOR macro. See the book for more X * information/details. X * X * Note that C++ constructs reference if needed, so this works: X * X * int i, rc; X * askfor x; X * rc = x.getwork(&i); X * X * This allows getprob() to fill out *prob, not just the pointer. In the X * above case, getprob() would do something like *(int*)prob = ; X */ X Xclass Askfor : public Barrier { X int done; // problem/program done flag X virtual getprob(void*&); // get-problem function X virtual void reset(); // logic-reset function X void wait(); // avoid confusion with Barrier::wait Xpublic: X Askfor(int Nprocs=0); // constructor X int getwork(void*&); // get a unit of work X void probend(int=2); // declare sub-problem done X void progend(); // declare program done X}; X Xinline void Askfor::wait() { Monitor::wait(); } X X#endif PARMACS_H SHAR_EOF if test 8494 -ne "`wc -c 'parmacs.h'`" then echo shar: error transmitting "'parmacs.h'" '(should have been 8494 characters)' fi echo shar: extracting "'pfinish.C'" '(388 characters)' if test -f 'pfinish.C' then echo shar: over-writing existing file "'pfinish.C'" fi sed 's/^X//' << \SHAR_EOF > 'pfinish.C' X#ifndef lint Xstatic char rcsid[] = "$Header: pfinish.C 1.2 88/04/04 $"; X#endif X X/* X * pfinish.C X * Default (NOP) parallel-finish-up function. X */ X X/* $Log: pfinish.C,v $ X */ X X/* X * pfinish() X * Called (single-stream) from main() after parallel program is done. X * X * This provides a (NOP) default, in case user program has no need for X * such finish-ups. X */ X Xvoid Xpfinish() X{ X /*NOP*/ X} SHAR_EOF if test 388 -ne "`wc -c 'pfinish.C'`" then echo shar: error transmitting "'pfinish.C'" '(should have been 388 characters)' fi echo shar: extracting "'pinit.C'" '(361 characters)' if test -f 'pinit.C' then echo shar: over-writing existing file "'pinit.C'" fi sed 's/^X//' << \SHAR_EOF > 'pinit.C' X#ifndef lint Xstatic char rcsid[] = "$Header: pinit.C 1.2 88/04/04 $"; X#endif X X/* X * pinit.C X * Default (NOP) parallel-init function. X */ X X/* $Log: pinit.C,v $ X */ X X/* X * pinit() X * Called from main() while still serial. X * X * This provides a (NOP) default, in case user program has no need for X * such initializations. X */ X Xvoid Xpinit(int, char**) X{ X /*NOP*/ X} SHAR_EOF if test 361 -ne "`wc -c 'pinit.C'`" then echo shar: error transmitting "'pinit.C'" '(should have been 361 characters)' fi echo shar: extracting "'shdelete.C'" '(272 characters)' if test -f 'shdelete.C' then echo shar: over-writing existing file "'shdelete.C'" fi sed 's/^X//' << \SHAR_EOF > 'shdelete.C' X#ifndef lint Xstatic char rcsid[] = "$Header: shdelete.C 1.2 88/04/04 $"; X#endif X X/* X * shdelete.C X * Shared-memory version of the delete operator. X */ X X/* $Log: shdelete.C,v $ X */ X Xextern shfree(char*); Xextern void operator delete(void* p) X{ X if (p) shfree( (char*)p ); X} SHAR_EOF if test 272 -ne "`wc -c 'shdelete.C'`" then echo shar: error transmitting "'shdelete.C'" '(should have been 272 characters)' fi echo shar: extracting "'shnew.C'" '(434 characters)' if test -f 'shnew.C' then echo shar: over-writing existing file "'shnew.C'" fi sed 's/^X//' << \SHAR_EOF > 'shnew.C' X#ifndef lint Xstatic char rcsid[] = "$Header: shnew.C 1.2 88/04/04 $"; X#endif X X/* X * shnew.C X * Shared-memory version of the new operator. X */ X X/* $Log: shnew.C,v $ X */ X Xtypedef void (*PFVV)(); X Xextern PFVV _new_handler; X Xextern void* operator new(long size) X{ X extern char* shmalloc(unsigned); X char* p; X X while ( (p=shmalloc(unsigned(size)))==0 ) { X if(_new_handler) X (*_new_handler)(); X else X return 0; X } X return (void*)p; X} SHAR_EOF if test 434 -ne "`wc -c 'shnew.C'`" then echo shar: error transmitting "'shnew.C'" '(should have been 434 characters)' fi echo shar: extracting "'slist.C'" '(2374 characters)' if test -f 'slist.C' then echo shar: over-writing existing file "'slist.C'" fi sed 's/^X//' << \SHAR_EOF > 'slist.C' X#ifndef lint Xstatic char rcsid[] = "$Header: slist.C 1.3 88/04/04 $"; X#endif X X/* X * slist.C X * C++ classes defining singly-linked-list manipulations. X * X * Motivated from C++ book, Sec 7.3 (not identical, though). X */ X X/* $Log: slist.C,v $ X */ X X#include "slist.h" X X/* X * SimpleList X * Simple, efficient singly-linked list. No dynamic allocation. X * Queue-able structures must be derived from "SimpleLink". X * X * Keeps circular list, with pointer to tail of list. tail->next is X * head of the list. X * X * append() and insert() are inline for efficiency. X */ X XSimpleLink* // inline here causes C++ bug (generates bad C) XSimpleList::get() X{ X if (tail == NULL) X return NULL; X else { X SimpleLink* head = tail->next; X if (head == tail) X tail = NULL; X else X tail->next = head->next; X return head; X } X} X Xvoid XSimpleList::clear() X{ X register SimpleLink* p; X X while (p = get()) delete p; // delete all queued objects X} X X/* X * GenList X * More general singly-linked list. X * Can enqueue any object, and given object multiple times. X * X * Keeps circular list, with pointer to tail of list. tail->next is X * head of the list. X * X * Uses queueing object (GenListQ), and caches these for efficiency. X */ X X/*inline*/ // C++ gens bad C if inline here XGenListQ* XGenList::newQ(void* Obj, GenListQ* p) // return new initialized Q object X{ X if (num_cache > 0) { X register GenListQ* Qobj = cache; X cache = Qobj->next; X --num_cache; X Qobj->next = p; X Qobj->obj = Obj; X return Qobj; X } else X return new GenListQ(Obj, p); X} X X/*inline*/ // inline here causes C++ bug (generates bad C) Xvoid* XGenList::get() // get entry from head of Q, NULL if none X{ X if (tail == NULL) X return NULL; X else { X GenListQ* head = tail->next; X if (head == tail) X tail = NULL; X else X tail->next = head->next; X void* val = head->obj; X cacheQ(head); X return val; X } X} X Xvoid XGenList::clear() // delete all queued objects X{ X register void* p; X X while (p = get()) delete p; X} X Xvoid XGenList::set_cache(int Max) // set size of cache X{ X while (num_cache > Max) { X GenListQ* Qobj = cache; X cache = Qobj->next; X --num_cache; X delete Qobj; X } X max_cache = Max; X} X XGenList::GenList(int MaxCache) // initialize the Q X{ X cache = tail = NULL; X max_cache = MaxCache; X num_cache = 0; X} X XGenList::~GenList() // destroy the Q X{ X clear(); // clear the Q X set_cache(0); // purge the cache X} SHAR_EOF if test 2374 -ne "`wc -c 'slist.C'`" then echo shar: error transmitting "'slist.C'" '(should have been 2374 characters)' fi echo shar: extracting "'slist.h'" '(2887 characters)' if test -f 'slist.h' then echo shar: over-writing existing file "'slist.h'" fi sed 's/^X//' << \SHAR_EOF > 'slist.h' X/* X * $Header: slist.h 1.2 88/04/04 $ X * X * slist.h X * C++ classes defining singly-linked-list manipulations. X * X * Motivated from C++ book, Sec 7.3 (not identical, though). X */ X X/* $Log: slist.h,v $ X */ X X#ifndef SLIST_H X#define SLIST_H X X#ifndef NULL X#define NULL 0 X#endif X X/* X * SimpleList X * Simple, efficient singly-linked list. No dynamic allocation. X * Queue-able structures must be derived from "SimpleLink". X * X * Keeps circular list, with pointer to tail of list. tail->next is X * head of the list. X * X * append() and insert() are inline for efficiency. X */ X Xstruct SimpleLink { X SimpleLink* next; X}; X Xclass SimpleList { X SimpleLink* tail; // end of list Xpublic: X void clear(); // purge the queue X void insert(SimpleLink*); // insert at head of list X void append(SimpleLink*); // append at end of list X SimpleLink* get(); // get an entry, NULL if none X SimpleList() { tail = NULL; } // initialize X ~SimpleList() { clear(); } // destroy X}; X Xinline void XSimpleList::insert(SimpleLink* elt) X{ X if (tail) { X elt->next = tail->next; X tail->next = elt; X } else { X tail = elt; X elt->next = elt; X } X} X Xinline void XSimpleList::append(SimpleLink* elt) X{ X if (tail) { X elt->next = tail->next; X tail = tail->next = elt; X } else { X tail = elt; X elt->next = elt; X } X} X X/* X * GenList X * More general singly-linked list. X * Can enqueue any object, and given object multiple times. X * X * Keeps circular list, with pointer to tail of list. tail->next is X * head of the list. X * X * Uses queueing object (GenListQ), and caches these for efficiency. X */ X Xstruct GenListQ { X GenListQ* next; X void* obj; X GenListQ(void* ent, GenListQ* p) { obj = ent; next = p; } X}; X Xclass GenList { X GenListQ* tail; // end of list; head=tail->next X int max_cache; // max Q objects in cache X int num_cache; // current # Q objects cached X GenListQ* cache; // cache of Q objects X GenListQ* newQ(void*, GenListQ*); // return initialized Q object X void cacheQ(GenListQ*); // cache or delete a Q object Xpublic: X void set_cache(int); // set max size of cache X void clear(); // purge the queue X void insert(void*); // insert at head of Q X void append(void*); // append at end of Q X void* get(); // get an entry, NULL if none X GenList(int MaxCache = 0); // initialize (cacheing off) X ~GenList(); // destroy the Q X}; X Xinline void XGenList::cacheQ(GenListQ* Qobj) // add to cache or delete, as approp X{ X if (num_cache < max_cache) { X Qobj->next = cache; X cache = Qobj; X ++num_cache; X } else X delete Qobj; X} X Xinline void XGenList::insert(void* elt) // insert at head of Q X{ X if (tail) X tail->next = newQ(elt, tail->next); X else { X tail = newQ(elt, NULL); X tail->next = tail; X } X} X Xinline void XGenList::append(void* elt) // insert at tail of Q X{ X if (tail) X tail = tail->next = newQ(elt, tail->next); X else { X tail = newQ(elt, NULL); X tail->next = tail; X } X} X X#endif SLIST_H SHAR_EOF if test 2887 -ne "`wc -c 'slist.h'`" then echo shar: error transmitting "'slist.h'" '(should have been 2887 characters)' fi echo shar: extracting "'workQ.h'" '(1833 characters)' if test -f 'workQ.h' then echo shar: over-writing existing file "'workQ.h'" fi sed 's/^X//' << \SHAR_EOF > 'workQ.h' X/* X * $Header: workQ.h 1.2 88/04/04 $ X * X * workQ.h X * C++ classes defining work-queue constructs on top of Askfor monitor. X */ X X/* $Log: workQ.h,v $ X */ X X#ifndef WORKQ_H X#define WORKQ_H X X#include "parmacs.h" // basic parallel-programming support X#include "slist.h" // singly-linked list support X X/* X * SimpleQ X * Simple work queue monitor -- uses SimpleList for queueing. X * X * Enqueues work elements derived from "work" base class. X * Simple/fast (no dynamic allocation of queueing objects), but limited X * in that work structures must be derived from "SimpleLink". X * X * Adds own getwork() interface for more type checking over Askfor. X */ X Xclass SimpleQ : public Askfor { X SimpleList queue; X int getprob(void*& prob) { X return (prob = (void*)queue.get()) != NULL; X } X void reset() { queue.clear(); } Xpublic: X int getwork(SimpleLink*& prob) { return Askfor::getwork(prob); } X void append(SimpleLink* prob) { enter(); queue.append(prob); signal(); } X void insert(SimpleLink* prob) { enter(); queue.insert(prob); signal(); } X SimpleQ(int Nprocs = 0) : (Nprocs) {} X}; X X/* X * GeneralQ X * More General work queue monitor -- uses GenList for queueing. X * X * Enqueues any type of object. Allows cacheing of queueing objects for X * efficiency. X * X * Takes and produces (void*) objects. More strongly typed interfaces can X * be derived from GeneralQ. X */ X Xclass GeneralQ : public Askfor { X GenList queue; X int getprob(void*& prob) { return (prob = queue.get()) != NULL; } X void reset() { queue.clear(); } Xpublic: X void append(void* prob) { enter(); queue.append(prob); signal(); } X void insert(void* prob) { enter(); queue.insert(prob); signal(); } X void set_cache(int Max) { enter(); queue.set_cache(Max); exit(); } X GeneralQ(int MaxCache = 0, int Nprocs = 0) : (Nprocs) { X queue.set_cache(MaxCache); X } X}; X X#endif WORKQ_H SHAR_EOF if test 1833 -ne "`wc -c 'workQ.h'`" then echo shar: error transmitting "'workQ.h'" '(should have been 1833 characters)' fi # End of shell archive exit 0