Java Threads mailing list archive

OCCAM-style java threads

From: Jeremy Martin <jeremy.martin@computing-services.oxford.ac.uk>
Date: Thu, 26 Sep 1996 14:15:28 +0100 (BST)

I've had a go at implementing occam-style constructs and point-to-point
channels in Java. My test program looks like this:

    --------------
    |  FEEDER    |\
    -------------- \
                    \ 
    --------------   |--------|
    |  FEEDER    |---| READER |
    --------------   |--------|
                    /
    -------------- /
    |  FEEDER    |/
    --------------

Three FEEDER processes continually send messages to a READER process which
communicates by ALTernation on its three input channels:

public class myprog {
    PAR NETWORK;
    public myprog() {
        channel[] A = { new channel("0"), new channel("1"), new channel("2")}; 
        Thread[] t = { new FEEDER(A[0],"nought"),
                    new FEEDER(A[1],"one"),
                    new FEEDER(A[2],"two") };
        PAR FEEDERS = new PAR(t);
        Thread[] u = {new READER(A), FEEDERS};
        NETWORK = new PAR (u);
    }
    static public void main (String[] args) {
        myprog m = new myprog();
        m.NETWORK.start();
        try {m.NETWORK.join();} catch (InterruptedException e) {};
    }
}

class FEEDER extends Thread {
    channel C;
    String N;
    public FEEDER(channel C2, String N2) {C = C2; N = N2;}
    public void run () {
        while (true) {
            System.out.println("*** :-? Feeding: "+N);
            C.write(N);
        }
    }
}

class READER extends Thread {
    channel[] A;
    public READER(channel[] A1) {A = A1;}
    public void run () {
        while (true) {
           String X = new String(); 
           READERBODY[] r = {new READERBODY(A[0]), 
                             new READERBODY(A[1]), 
                             new READERBODY(A[2])};
           ALT k = new ALT(r);
           k.start();
           try {k.join();} catch (Throwable e) {};
        }
    }
}

class READERBODY extends guardedthread {
    public READERBODY(channel C) {super(C);};
    public void run() {System.out.println("*** :-) Read: "+readobject);}
}

The SEQ and PAR constructs are fairly easily defined:

// Implementation of OCCAM SEQ process.

public class SEQ extends Thread {
    private Thread[] t;
    public SEQ (Thread[] t2) {
        t = t2;
    }
    public void run () {
        for (int i = 0; i < t.length; i++) {
            t[i].start();
            try {t[i].join();} catch (InterruptedException e) {}
        }
    }
}

// Implementation of OCCAM PAR process.

public class PAR extends Thread {
    private Thread[] t;
    public PAR (Thread[] t2) {
        t = t2;
    }
    public void run () {
        for (int i = 0; i < t.length; i++) {
            t[i].start();
        }
        for (int i = 0; i < t.length; i++) {
            try {t[i].join();} catch (InterruptedException e) {}
        }
    }
}

ALT is more complicated and requires a "guarded thread" class to be
defined.

// Implementation of OCCAM ALT process

public class ALT extends Thread {
    private guardedthread[] g;
    public ALT (guardedthread[] g2) {
        g = g2;
    }
    public void run () {
        while (true) {
            synchronized (this) {
                for (int i = 0; i < g.length; i++) { // Try each branch in
                    if (g[i].guard) {                // turn. If none is
                        if (g[i].chan == null) {     // ready wait to be
                            g[i].start();     // notified.
                            try {g[i].join();} 
                            catch (InterruptedException e) {}
                            return;
                        }
                        else if (g[i].chan.ready(this)) {
                            g[i].readobject = g[i].chan.read();
                            g[i].start();
                            try {g[i].join();} 
                            catch (InterruptedException e) {}
                            return;
                        }
                    }
                }
                System.out.println("ALT process waiting");
                try {wait ();} catch (InterruptedException e) {};
            }
        }
    }
}
   
// Implementation of OCCAM guarded process (BOOL)& a?x -> P.
// Used only as a branch of an ALT process.

public class guardedthread extends Thread {
    public boolean guard;
    public channel chan;
    public Object readobject;
    public guardedthread(boolean g, channel c) {
        guard = g; chan = c;
    }
    public guardedthread(channel c) {
        guard = true; chan = c;
    }
    public guardedthread() {
        guard = true; chan = null;
    }
}

The channel definition is a simplified version of those discussed at the
workshop

// Implementation of OCCAM channel

public class channel {
    boolean donewriting, waitingALTprocess;
    ALT ALTprocess;
    String name;
    private Object o;
    public channel() {
        name = "DEFAULT";
        donewriting = false;
        waitingALTprocess = false;
    }
    public channel(String name2) {
        name = name2;
        donewriting = false;
        waitingALTprocess = false;
    }
    public synchronized Object read() { // Read from a channel
        if (!donewriting) {
            System.out.println("Waiting to read on channel "+name);
            try {
                wait (); // Wait in channel queue until woken up 
            }            // by method write
            catch (InterruptedException e) {};
        }
        System.out.println("Reading on channel "+name);
        donewriting = false;
        notify ();
        return o; 
    }
    public synchronized void write (Object wo) { // Write to a channel
        System.out.println("Writing on channel "+name);
        o = wo;
        donewriting = true;
        notify ();
        if (waitingALTprocess) {        // If an ALT construct is waiting
            synchronized (ALTprocess) { // to input from the channel
                System.out.println("Waiting ALT process notified by "+name);
                ALTprocess.notify();    // then notify it.
                waitingALTprocess = false;
                ALTprocess = null;
            }
        }
        System.out.println("Waiting to exit write method on channel "+name);
        try {        // Now wait until the message has been read 
            wait (); // before returning.
        }
        catch (InterruptedException e) {};
        System.out.println("Exiting write method on channel "+name);
        return;
    }
    public synchronized boolean ready (ALT a) {
    // An ALT process checks to see wheth the channel is ready to output
        System.out.println("Seeing if ready channel "+name);
        if (donewriting) {  
            return true;
        }
        else {
            waitingALTprocess = true;
            ALTprocess = a;
            return false;
        }
    }
}

Here I am assuming that a channel may only be used by two concurrent threads
at any time. One for input the other for output. I am also banning 
output guards from ALT statements. (This means that when a process is
ready to output on a channel there can be no other option for its 
behaviour).

When the outputting thread wishes to write to the channel it invokes the 
write method. It is not allowed to return until the message has been read 
by the inputting process. Similarly a reading process must wait until a 
message becomes available. Alternation is more complicated. An ALT 
process checks to see whether any of its input channels are ready? If not 
it goes to sleep and asks each of the input channels to wake it up when 
they are ready. As soon as a channel becomes ready it performs a read 
method on that channel. 

Here is some sample output from the program, interleaved messages fron the
various channels and the ALT process, with all the debugging messages included:

        Seeing if ready channel 0
                 Seeing if ready channel 1
                          Seeing if ready channel 2
ALT process waiting
                                                      *** :-? Feeding: nought
        Writing on channel 0
        Waiting ALT process notified by 0
        Waiting to exit write method on channel 0
                                                      *** :-? Feeding: one
                 Writing on channel 1
                                                      *** :-? Feeding: two
                          Writing on channel 2 
                 Waiting ALT process notified by 1
                 Waiting to exit write method on channel 1
                          Waiting ALT process notified by 2
                          Waiting to exit write method on channel 2
        Seeing if ready channel 0
        Reading on channel 0
        Exiting write method on channel 0
                                                      *** :-? Feeding: nought
        Writing on channel 0
        Waiting to exit write method on channel 0
                                                      *** :-) Read: nought
        Seeing if ready channel 0
        Reading on channel 0
        Exiting write method on channel 0
                                                      *** :-? Feeding: nought
        Writing on channel 0
        Waiting to exit write method on channel 0
                                                      *** :-) Read: nought
        Seeing if ready channel 0
        Reading on channel 0
        Exiting write method on channel 0

Note that my ALT is currently unfair, so channel 0 is always favoured.

I'd be interested to know what people think about the potential for this 
slightly different approach.

							Jeremy

	


Last updated: Tue Nov 2 12:11:28 1999
Maintained by Peter Welch.