Hi Peter and Thomas, The new version of CTJ (version 0.9 rev. 14) contains some new useful features: - improved output guards (they were implemented in version 0.9 rev. 6 and temporarily removed because of implementation difficulties) - CTJ channels support CRCR (Concurrent-Read-Concurrent-Write) by default and they can be enabled for exclusive use. CTJ channels can be set to CRCW, ERCW, CREW or EREW (Concurrent/Exclusive-Read-Concurrent/Exclusive-Write) using the claimInput()-releaseInput() methods for Exclusive-Read and claimOutput()-releaseOutput() for Exclusive-Write (the example below will show the use of these methods), - updated TCPIP and UDP link drivers are available (where disabled since rev.10), - the select() method is back (was protected since rev.10 and now public available), - new Guards such as SKIP-guards and TIMEOUT-guards are implemented, - new STOP() and SKIP() processes are included, - all wrappers (Integer, Byte, Short, Float, Double, ..) are almost similar to the Java wrappers and contain the same set of useful methods, - several bug-fixes in the kernel. Some of these features enable you to create CALL channels. Creating CALL channels with CTJ is almost similar to Peter's approach using JCSP. The example below shows some additional features, such as - multiple return arguments, - exclusive channel communication, - guarded CALL channels, - compositional and select() approach, - separates process address spaces by copying object data during communication, - and support of 'remote' CALL channels. In this example, the CALL channel performs z,w = f(x,y). CALL CHANNEL INTERFACES ~~~~~~~~~~~~~~~~~~~~~~~ interface CallChannelClient_of_Example { public void callMethod(Integer x, Integer y, Double z, Boolean w); } The arguments x and y are input arguments and the arguments z and w are output arguments of the callMethod(...) method. interface CallChannelServer_of_Example { public void read(Integer x, Integer y); public void write(Double z, Boolean w); public ChannelInput guardChannel(); } Perhaps, the read(x,y) and write(z,w) could be renamed in something like getArguments(x,y) and accept(z,w). CALL CHANNEL CLASS ~~~~~~~~~~~~~~~~~~ class CallChannel_of_Example implements CallChannelClient_of_Example, CallChannelServer_of_Example { private Channel_of_Object toServer; private Channel_of_Object fromServer; /** * Create a CallChannel. **/ public CallChannel_of_Example() { toServer = new Channel_of_Object(); fromServer = new Channel_of_Object(); } /** * Create a 'remote' CallChannel using link drivers. **/ public CallChannel_of_Example(LinkDriver ldToServer, LinkDriver ldFromServer) { toServer = new Channel_of_Object(ldToServer); fromServer = new Channel_of_Object(ldFromServer); } /** * Perform call method. **/ public void callMethod(Integer x, Integer y, Double z, Boolean w) { // claim channels -- Exclusive Read and Exclusive Write (EREW) //------------------------------------------------------------- toServer.claimOutput(); // claim toServer channel fromServer.claimInput(); // claim fromServer channel toServer.write(x); // 'toServer' is the guard channel toServer.write(y); fromServer.read(z); fromServer.read(w); // release channels -- Concurrent Read and Concurrent Write (CRCW) //----------------------------------------------------------------- toServer.releaseOutput(); // release toServer channel fromServer.releaseInput(); // release fromServer channel } /** * Read all input arguments from client. **/ public void read(Integer x, Integer y) { // claim channels -- Exclusive Read and Exclusive Write (EREW) //------------------------------------------------------------- toServer.claimInput(); // claim toServer input channel fromServer.claimOutput(); // claim fromServer output channel toServer.read(x); toServer.read(y); } /** * Write results to client. **/ public void write(Double z, Boolean w) { fromServer.write(z); fromServer.write(w); // release channels -- Concurrent Read and Concurrent Write (CRCW) //----------------------------------------------------------------- toServer.releaseInput(); // release toServer input channel fromServer.releaseOutput(); // release fromServer output channel } /** * Return guard channel. **/ public ChannelInput guardChannel() { return (ChannelInput)toServer; } } Many clients could share the CALL channel 'CallChannel_of_Example'. In that case, the callMethod() must be synchronised using the Java 'synchronized' keyword. However, this is not sufficient when the toServer or fromServer channels are shared by other processes. Instead of using the 'synchronized' keyword, I used a claim-release construct (see claimInput()-releaseInput() and claimOutput()-releaseOutput() methods) that enforces an exclusive claim of shared channels. One should be careful when using the Java 'synchronized' construct with channels. The channel read(..) and write(..) methods inside a monitor can lock the monitor when they block the process and wait for communication. Channels do not release (or unlock) the outer monitor when they block. When the outer monitor locks then all other related monitors will also lock. The program may deadlock. The claim-release construct provides a solution to this problem. Example of the claim-release construct -------------------------------------- Two processes, e.g. Process1 and Process2, want each to write two objects in sequence to a shared channel. The reading by the reader process may be partitioned and the results can be wrong. PAR SEQ // Process1 channel.write(a); channel.write(b); SEQ // Process2 channel.write(q); channel.write(r); SEQ // a reader process channel.read(x); channel.read(y); The results will be x = a and y = b or x = q and y = r or x = a and y = q or x = q and y = a The claim-release construct provides exclusive access of channel input or channel output. PAR SEQ // Process1 channel.claimOutput(); channel.write(a); channel.write(b); channel.releaseOutput(); SEQ // Process2 channel.claimOutput(); channel.write(q); channel.write(r); channel.releaseOutput(); SEQ // a reader process channel.read(x); channel.read(y); The results will be x = a and y = b or x = q and y = r There is still a race competition between Process1 and Process2, but the writing does not partition. When Process1 first claims the channel output by invoking channel.claimOutput() then Process2 will block when it invokes channel.claimOutput() before Process1 releases the channel output. Process2 continues when Process1 invokes the channel.releaseOutput() method and claims the channel output. A problem is that the programmer can forget to claim a channel input or output. For instance, if you take out the channel.claimOutput() and channel.releaseOutput() in Process2 and the channel output was claimed by Process1 then an exception will be thrown on the first channel.write(q) statement by Process2. If Process2 first executes channel.write(q) and then Process1 claims the channel output an exception will be thrown by the second channel.write(r) statement. When the processes execute in some sequence (i.e. only context switching at termination of the processes) no exceptions will be thrown and everything works fine. The claim-release construct must be used in a proper way! Claiming and releasing channel inputs or channel outputs is not required. The CTJ channel read() and write() methods will throw an exception (e.g. NotOwnerException) when they are invoked but claimed by another owner. The read() and write() methods will not throw an exception when the channel input or channel output was not claimed. The process that claims a channel input or channel output is the owner of the read() or write() method. APPLICATION ~~~~~~~~~~~ Shared CALL channel by server and client ----------------------------------------- CallChannelServer_of_Example callchannel = new CallChannel_of_Example(); or with buffer link drivers CallChannelServer_of_Example callchannel = new CallChannel_of_Example(new Buffer(10), new Buffer(10)); Other link drivers, such as TCPIP or UDP, can be plugged in. Server Application ------------------ // COMPOSITIONAL APPROACH class ServerExample implements csp.lang.Process { Alternative alt; public ServerExample(CallChannelServer_of_Example channel) { alt = new Alternative(new Guard[] { new guard(callchannel.guardChannel(), new Process() { CallChannel_of_Example callchannel = channel; Integer x = new Integer(); Integer y = new Integer(); Double z = new Double(); Boolean w = new Boolean(); int divideFactor = 2; // the callMethod method performing z,w = f(x,y) public void run() { callchannel.read(x,y); z.value = (x.value + y.value)/divideFactor; w.value = z.value > 10; callchannel.write(z,w); } }), ... // other guards }); public void run() { while(true) { alt.run(); } } } // SELECT APPROACH class ServerExample implements csp.lang.Process { CallChannelServer_of_Example callchannel; Alternative alt; // declare local objects/memory Integer x = new Integer(); Integer y = new Integer(); Double z = new Double(); Boolean w = new Boolean(); int divideFactor = 2; public ServerExample(CallChannelServer_of_Example channel) { this.callchannel = channel; alt = new Alternative(new Guard[] { new guard(callchannel.guardChannel()), ... }); } public void run() { while(true) { switch(alt.select()) { case 0: // the callMethod method peforming z,w = f(x,y) callchannel.read(x,y); z.value = (x.value + y.value)/divideFactor; w.value = z.value > 10; callchannel.write(z,w); break; case ?: ... other processes } } } } The read() method receives all input data and the write() method returns all result data. Client Application ------------------ class ClientExample implements csp.lang.Process { CallChannelClient_of_Example callchannel; public Client(CallChannelClient_of_Example channel) { callchannel = channel; } public void run() { // declare local objects/memory Integer x = new Integer(); Integer y = new Integer(); Double z = new Double(); Boolean w = new Boolean(); // fill in messages x.value = 100; y.value = 23; // do the call callchannel.callMethod(x,y,z,w); // results: // z.value = (100+23)/2 = 61.5; // w.value = (z > 10) = true; } } I hope that this approach can be helpful to you. I appreciate any comments. Gerald. PS. The class/interface names CallChannel_of_Example, CallChannelClient_of_Example, CallChannelServer_of_Example, Channel_of_Object, ChannelInput and all others are chosen so that channels and CALL channels easily can be found in the source code. All channel classes/interfaces begin with the word "Channel" and CALL channel classes/interfaces begin with the word "CallChannel". For class/interface names they may seem vague but to me this naming convention is practical. However, they can be changed to something else. I'm open for suggestions.