Hi Peter and Gerald, your ideas are quite good. In fact my first ideas were similar to yours. But the major drawback of this approach is, that you need either on the client or on the server side a pair of corresponding read/write statements. The compiler unfortunately cannot check their presence. If the programmer forgets one of these, the system will hang :-( My approach is different. On the client side I use a "write" command similar to the one we already have in ordinary channels, except that it returns an object; that is straight forward and trivial: public Object write (Object object); The trick is on the server side where the "read" method gets a new interface as parameter: public Object read (CallChannelFunctionInterface function); This interface is defined as follows: public interface CallChannelFunctionInterface { public Object accept (Object in); } How does "read" work? It performs three steps: 1. "read" reads the object (call it "inObj") sent by "write" (no difference to ordinary channels), 2. after that, the "accept" method is called with "inObj" as parameter, 3. the return object of "accept" is passed back to the "write" method. For this the "ChannelDataStore" used in JCSP has to be extended to store the return value. With this extention "read" and "write" could look like this: public synchronized Object read (CallChannelFunctionInterface function) { if (data.getState () == ChannelDataStore.EMPTY) { try { wait (); } catch (InterruptedException e) { } } Object value = data.get (); data.putReturn (function.accept(value)); // this is new notify (); return value; } public synchronized Object write (Object value) { Object returnObject = null; // this is new data.put (value); if (alt != null) { alt.schedule (); } else { notify (); } try { wait (); returnObject = data.getReturn (); // this is new } catch (InterruptedException e) { } return returnObject; // this is new } May be, that there is some extra wait/sync needed. But the idea should work (at least I hope so). The user doesn't see the details within "read" and "write". He has only to provide the implementation of the new interface, where the compiler checks the details. With only minor modifications in "read" and "write" we can use the One2OneChannel, Many2OneChannel, etc. in the same way as we do now (even remote channels and AltingInput will work). I am testing a modified version of "Whot, no chickens": The Phil code is like this: ChannelOutput request; Object chickens; int wanted = 2; ... chickens = request.write (new Integer (wanted)); // Phil wants 2 chickens The Canteen looks like this: AltingChannelInput request; PileOfChickens pileOfChickens = new PileOfChickens (); ... switch (alt.fairSelect (preCondition)) { case REQUEST: Integer wanted = (Integer) request.read (pileOfChickens); // "read" reads the input and sends the return value! // or if you don't need the trasmitted number simply say: // request.read (pileOfChickens); break; case SUPPLY: ... The "PileOfChickens" is luxury version of "CallChannelFunctionInterface": class PileOfChickens implements CallChannelFunctionInterface { private int nChickens; // constructor PileOfChickens () { this.nChickens = 0; } // the return function of out CALL channel: public Object accept (Object input) { int wanted = ((Integer) input).intValue(); int ok = min (wanted, nChickens); // may be, not enough chickens there nChickens -= ok; return new Integer (ok); } // get the actual size int size () { return nChickens; } // add some chickens void add (int incr) { nChickens += incr;} int min (int a, int b) { if (a < b) return a; else return b;} } What do you think of this? Have I overseen something? Thomas