CSP for Java
(JCSP) 1.1-rc4

org.jcsp.lang
Class One2OneCallChannel

java.lang.Object
  extended by org.jcsp.lang.Guard
      extended by org.jcsp.lang.AltingChannelAccept
          extended by org.jcsp.lang.One2OneCallChannel
All Implemented Interfaces:
Serializable, ChannelAccept

public abstract class One2OneCallChannel
extends AltingChannelAccept
implements Serializable

This is the super-class for one-to-one interface-specific CALL channels.

Shortcut to the Constructor and Method Summaries.

Description

Normal method invocation between objects is sometimes refered to as message-passing between a client (the invoker of the method) object and a server object (the invoked). Information flows from the client (the method name and parameter values) to the server, which reacts in some way (possibly changing state) and may return information back to the client (via the method result or, indirectly, by changing values referenced by the method parameters).

This corresponds loosely with the client-server communication pattern between active processes, but where information flows over a pair of channels connecting them. However, there are major semantic differences between the two mechanisms. A server process is in charge of its own life, can act spontaenously and can refuse to accept a client call. A server object is passive, never does anything unless invoked and, then, has no choice but to obey. We must be careful, therefore, not to become confused by the same words computer scientists have chosen to mean very different things.

CALL channels risk deepening this confusion. They provide a method interface for client-server communication between active processes, yet their semantics remain those of a synchronising zero-buffered channel. However, the attraction of replacing a sequence of channel write(s) and read (matched at the other end of the channel-pair by a sequence of channel read(s) and write) with a single method invocation (and a single matching accept) makes this risk worthwhile.

[Note: CALL channels were part of the occam3 language specification. They provide an extended rendezvous in the sense of an Ada entry, but are considerably more flexible and lightweight.]

Converting a Method Interface into a Variant CALL Channel

A CALL channel must be individually constructed to support a specific method interface. This is done by extending this class to implement that interface in the manner outlined below. The calling (i.e. client) process just sees that interface and invokes its methods. The callee (i.e. server) process just sees the accept method of the CALL channel and invokes that (supplying itself as argument). Both actions are voluntary and have to occur for the communication to take place.

Consider the following:

 interface Foo {
 
   public Bar calculate (...);
   public void processQuery (...);
   public boolean closeValve (...);
 
 }
 
Deriving the corresponding CALL channel is mechanical and could easilly be automated:
 import org.jcsp.lang.*;
 
 public class One2OneFooChannel extends One2OneCallChannel implements Foo {
 
   public static final int CALCULATE = 0;           // optional
   public static final int PROCESS_QUERY = 1;       // optional
   public static final int CLOSE_VALVE = 2;         // optional
 
   public Bar calculate (...) {
     join ();                                       // ready to make the CALL
     Bar result = ((Foo) server).calculate (...);
     selected = CALCULATE;                          // optional
     fork ();                                       // call finished
     return result;
   }
 
   public void processQuery (...) {
     join ();                                       // ready to make the CALL
     ((Foo) server).processQuery (...);
     selected = PROCESS_QUERY;                      // optional
     fork ();                                       // call finished
   }
 
   public boolean closeValve (...) {
     join ();                                       // ready to make the CALL
     boolean result = ((Foo) server).closeValve (...);
     selected = CLOSE_VALVE;                        // optional
     fork ();                                       // call finished
     return result;
   }
 
 }
 
The above methods will be called by the client process. The
join will not complete until the server invokes an accept on this channel. That accept will not complete until the client reaches the fork. This gives the zero-buffered fully synchronised semantics of CSP channel communication.

In between the join and the fork, the client and server processes are locked together in extended rendezvous, commonly executing the chosen method in the server environment. [Actually, it is the client that invokes the method on the server, temporarilly referenced by the server field from this One2OneCallChannel super-class and blocked waiting for its accept to complete.] Setting the selected field is optional. However, its value is returned to the server by the accept method and can be used (as in the above) to let the server know which of its methods the client invoked.

[Note: a One2OneFooChannel is only safe to use between a single client and a single server. Any-1, 1-Any and Any-Any versions may be derived from Any2OneCallChannel, One2AnyCallChannel and Any2AnyCallChannel (respectively) using exactly the same pattern as above.]

Calling a CALL Channel

All the client needs to see is the method interface implemented by the CALL channel. For example:
 import org.jcsp.lang.*;
 
 public class A implements CSProcess {
 
   private final Foo out;
 
   public A (final Foo out) {
     this.out = out;
   }
 
   public void run () {
     ...
     Bar t = out.calculate (...);
     ...
     out.processQuery (...);
     ...
     if (! out.closeValve (...)) ...;
     ...
   }
 
 }
 
[Note: this means, of course, that a client is blind to the variety of CALL channel it is calling. It may be connected to its server(s) via a 1-1, Any-1, 1-Any or Any-Any link without any change in its coding.]

Accepting a CALL Channel

To receive the calls forwarded by the CALL channel, the server needs to implement the same interface. To accept a call, it invokes the accept method of the CALL channel, passing itself (usually this) as the argument. All it needs to see, therefore, is the ChannelAccept interface implemented by the channel. For example:
 import org.jcsp.lang.*;
 
 class B implements CSProcess, Foo {
 
   private final ChannelAccept in;
 
   public B (final One2OneFooChannel in) {
     this.in = in;
   }
 
   ...  other fields, methods etc.
 
   ...  implementation of Foo methods
 
   public void run () {        // controls when Foo invocations are acceptable
     ...
     in.accept (this);                   // don't care which method was called
     ...
     switch (in.accept (this)) {         // care which method was called
       case One2OneFooChannel.CALCULATE:
         ...
       break;
       case One2OneFooChannel.PROCESS_QUERY:
         ...
       break;
       case One2OneFooChannel.CLOSE_VALVE:
         ...
       break;
     }]
     ...
     in.accept (this);                   // don't care which method was called
     ...
   }
 
 }
 
However, it is not very secure for a server process (like B) to advertise a standard method interface (like Foo). In the above example, there is the danger that someone might try to invoke one of the Foo methods directly on an instance of B (e.g. by plugging an instance of B, instead of the CALL channel, into an instance of A). That would not be a good idea!

It is also semantically misleading - B's interface is through the CALL channel passed to its constructor, not through its (necessarilly public) Foo methods.

So, B should not be the public server process - we need to hide its directly invocable methods. A simple way to do this is to wrap it up in another process that simply omits the public declaration of the relevant interface:

 import org.jcsp.lang.*;
 
 public class B2 implements CSProcess {            // no Foo interface
 
   private final B b;
 
   public B2 (final One2OneFooChannel in) {
     b = new B (in);
   }
 
   public void run () {
     b.run ();
   }
 
 }
 
Notice that this wrapper imposes no run-time overhead, apart from a small start-up cost. The hidden inner process does all the work and has direct access to the CALL channel and, hence, to the client.

[Note: the only difference needed in the server code to support Any-1, 1-Any and Any-Any CALL channels is in the parameter declaration that specifies the variety to be used. For complete flexibility, constructors (or setFooChannel methods) for each kind (i.e. One2OneFooChannel, Any2OneFooChannel, One2AnyFooChannel and Any2AnyFooChannel may be provided.]

ALTing on a CALL Channel

This class is a sub-class of Guard and, therefore, its derived CALL channels may be included in a Guard array associated with an Alternative. Hence, the server may ALT between any mixture of CALL channels, ordinary channels, timeouts and Skips.

However, when implementing the server, the CALL channel field needs to be declared as an AltingChannelAccept, rather than a ChannelAccept. So, in the above example, the first field declaration of B needs to become:

   private final AltingChannelAccept in;
 
See Any2OneCallChannel for an example of ALTing between CALL channels.

[Note: a server may ALT on a Any-1 CALL channel with this same change. However, as for ordinary channels, ALTing over 1-Any or Any-Any versions is not supported.]

Building a CALL Channel Network

Network building with CALL channels is the same as building with ordinary channels. First construct the channels and, then, construct the processes - plugging in the channels as required and running them in Parallel.

For example, the simple two process network:

is implemented by:
     One2OneFooChannel c = new One2OneFooChannel ();
 
     new Parallel (
       new CSProcess[] {
         new A (c),
         new B2 (c)
       }
     ).run ();
 

[Note: simple network examples using Any-1, 1-Any and Any-Any CALL channels are given in their respective classes.]

Example

Please see Any2OneCallChannel for an example that includes one server, ALTing between two (Any-1) CALL channels, and lots of clients.

Author:
P.H. Welch
See Also:
Any2OneCallChannel, One2AnyCallChannel, Any2AnyCallChannel, Alternative, Serialized Form

Field Summary
protected  int selected
          This may be set during the standard calling sequence to record which method was invoked by a client.
protected  CSProcess server
          This holds a reference to a server process so that a client may make the call.
 
Constructor Summary
One2OneCallChannel()
           
 
Method Summary
 int accept(CSProcess server)
          This is invoked by a server when it commits to accepting a CALL from a client.
protected  void fork()
          This is invoked by a client during the standard calling sequence.
protected  void join()
          This is invoked by a client during the standard calling sequence.
 
Methods inherited from class org.jcsp.lang.Guard
schedule
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

server

protected CSProcess server
This holds a reference to a server process so that a client may make the call. The reference is only valid between the join and fork elements of the standard calling sequence. As shown in that sequence, it will need casting up to the relevant interface supported by the specific CALL channel derived from this class.


selected

protected int selected
This may be set during the standard calling sequence to record which method was invoked by a client. It is only safe to do this between the join and fork elements of that sequence. Either all the CALL channel methods should do this or none - in the latter case, its default value remains as zero. Its value is returned to a server as the result the server's invocation of accept.

Constructor Detail

One2OneCallChannel

public One2OneCallChannel()
Method Detail

accept

public int accept(CSProcess server)
This is invoked by a server when it commits to accepting a CALL from a client. The parameter supplied must be a reference to this server - see the example above. It will not complete until a CALL has been made. If the derived CALL channel has set the selected field in the way defined by the standard calling sequence, the value returned by this method will indicate which method was called.

Specified by:
accept in interface ChannelAccept
Parameters:
server - the server process receiving the CALL.

join

protected void join()
This is invoked by a client during the standard calling sequence. It will not complete until a server invokes an accept on this channel. In turn, that accept will not complete until the client invokes a fork, after having made its CALL on the server.


fork

protected void fork()
This is invoked by a client during the standard calling sequence. A server must have invoked an accept for the client to have got this far in the sequence - see the join. This call unblocks that accept, releasing the server and client to resume separate lives.


CSP for Java
(JCSP) 1.1-rc4

Submit a bug or feature to jcsp-team@kent.ac.uk
Version 1.1-rc4 of the JCSP API Specification (Copyright 1997-2008 P.D.Austin and P.H.Welch - All Rights Reserved)
Java is a trademark or registered trademark of Sun Microsystems, Inc. in the US and other countries.