ABSTRACT: XDP is a simplified interface to the DP distributed programming library. I describe its use in a course on workstation programming, a pragmatic course whose mission is to cover concurrent programming, graphical user interfaces and event-driven programming as well as network and distributed computing. Using XDP, rather than the native socket interface, makes it feasible to cover the last topics, squeezed though they are into a rather overloaded course. Finding (or building) teaching tools like XDP will become increasingly essential as more demands are placed on undergraduate CS curriculum coverage.
Publication Information: This Brooklyn College Technical Report was presented at the SIGCSE Technical Symposium during ACM Week 1995 and appeared in the conference proceedings for that symposium: XDP: A simple library for teaching a distributed programming module. Proceedings of the 26th SIGCSE Technical Symposium, Nashville, Tennessee, March 1995.
Acknowledgment of Support: This work was in part supported by PSC-CUNY grants from the City University of New York.
xdpwrite(id, msgptr, msgsize, mode)
where id is an integer that identifies the target, msgptr points to the
message data, msgsize is the size of the message, and mode is a set of flags
that allow two orthogonal choices:xdprecv(src, buffer, limit, mode) xdpgetmsg(src, buffer, limit)In each case, src is a pointer to an integer in which the id of the sender will be stored. The second argument, buffer is the address of a buffer in which to store the message contents and the third argument, limit specifies its size. Messages that exceed this limit are truncated without remark. The xdpgetmsg() call is used to receive messages that generate an interrupt- as such it is typically used inside an interrupt handler (see below). In such a context, blocking would be inappropriate, and so xdpgetmsg() never blocks- it returns immediately, with the return value XDPSUCCESS or XDPNOMESSAGE. On the other hand, by using a mode of XDPBLOCK or XDPNOBLOCK, the xdprecv() call may or may not be forced to block until a message is available.
while (xdpgetmsg(...)!=XDPNOMESSAGE) process message just received;
xdppause(t, f)This call causes the invoking process to suspend execution until an asynchronous event has taken place. Such events include arrival of an interrupting message, a timeout or an external signal. If t is not zero, a timeout event is set to occur in t milliseconds. In that case, if f is not null, it is a function that will be invoked prior to return from xdppause(). If t=0 no timeout event will take place. Since the xdppause() routine returns as a result of any asynchronous event, interrupting message arrival or otherwise, so the necessary condition must be rechecked:
This code guarantees that the test-and-call sequence (test the condition, call xdppause()) is atomic. The message handler is blocked from the time the condition is tested through the time xdppause() is entered. In order for xdppause() to ever have a chance to complete and to make it possible for the desired condition to become true, xdppause() reenables message handler invocations upon enter. Upon return from xdppause(), the message handler status is restored to its state upon entry. Note that during xdppause(), interrupts are automatically re-enabled.xdpblock(); while (!desiredcondition) xdppause(t, f); xdpunblock();
Along with examples like this students have been given various exercises, such as:#include <stdio.h> #include "xdp.h" #define REL_RECV (XDPREL|XDPRECV) #define REL_INT R (XDPREL|XDPGETMSG) #define MAXPRIMES 90000 int p[MAXPRIMES], nprimes=0; int mypid, nnodes, done=0, interval, maxnum; void /* executed by the primary only */ newprime(int n) { xdpblock(); /* potential race condition */ if (nprimes<MAXPRIMES) /* so block interrupts */ p[nprimes++] = n; xdpunblock(); } void sendint(int dest, int v, int mode) { xdpwrite(dest, v, sizeof(i), REL_INTR); } void /* executed by the primary only */ fcatch() { int p, src; while (xdpgetmsg(&src,&p,sizeof(p)) !=XDPNOMESSAGE) if (p < 0) done++; else newprime(p); } void search(int n1,int n2) { int i; for (i=n1; i<=n2; i++) { if (IsPrime(i) { if (mypid==0) newprime(i); else sendint(0,i,REL_INTR); } } } void foreman() { /* executed by the primary only */ int i, pid, b; xdpcatchmsg(fcatch); for (b=0, pid=1; pid<nnodes; pid++) { sendint(pid,b, REL_RECV); b += interval; } search(b,maxnum); done++; xdpblock(); while (done<nnodes) xdppause(0L, NULLFUNC); xdpunblock(); ... print the list of primes here ... xdpexit("You're fired!"); } void worker() { /* executed by the secondary only */ int b, src; xdprecv(&src, &b, sizeof(b), XDPBLOCK); search(b, b+interval); quitvalue = -1; sendint(0,-1,REL_INTR); fclose(fp); xdpexit("I quit!"); } main(int ac,char *av[]) { nnodes=xdpinit(&ac,&a); maxnum = atoi(av[1]); mypid = xdpgetpid(); interval = maxnum/nnodes; (mypid == 0) ? foreman() : worker(); }