Class One2OneCallChannel
- All Implemented Interfaces:
Serializable
,ChannelAccept
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 theaccept
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 theaccept
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 ofGuard
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 inParallel
.
For example, the simple two process network:
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.-
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final One2OneChannelImpl
This is used to synchronise the calling and accepting process.protected int
This may be set during the standard calling sequence to record which method was invoked by a client.protected CSProcess
This holds a reference to a server process so that a client may make the call. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionint
This is invoked by a server when it commits to accepting a CALL from a client.(package private) boolean
disable()
This is one of theGuard
methods needed by theAlternative
class.(package private) boolean
enable
(Alternative alt) This is one of theGuard
methods needed by theAlternative
class.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.
-
Field Details
-
c
This is used to synchronise the calling and accepting process. -
server
This holds a reference to a server process so that a client may make the call. The reference is only valid between thejoin
andfork
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 selectedThis 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 thejoin
andfork
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 ofaccept
.
-
-
Constructor Details
-
One2OneCallChannel
public One2OneCallChannel()
-
-
Method Details
-
accept
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 theselected
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 interfaceChannelAccept
- 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 anaccept
on this channel. In turn, that accept will not complete until the client invokes afork
, 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 anaccept
for the client to have got this far in the sequence - see thejoin
. This call unblocks that accept, releasing the server and client to resume separate lives. -
enable
This is one of theGuard
methods needed by theAlternative
class. -
disable
boolean disable()This is one of theGuard
methods needed by theAlternative
class.
-