Class Any2OneCallChannel
- java.lang.Object
-
- org.jcsp.lang.Guard
-
- org.jcsp.lang.AltingChannelAccept
-
- org.jcsp.lang.Any2OneCallChannel
-
- All Implemented Interfaces:
Serializable
,ChannelAccept
public abstract class Any2OneCallChannel extends AltingChannelAccept implements Serializable
This is the super-class for any-to-one interface-specific CALL channels, safe for use by many clients and one server.Shortcut to the Constructor and Method Summaries.
Description
Please seeOne2OneCallChannel
for general information about CALL channels. Documented here is information specific to this any-1 version.Converting a Method Interface into a Variant CALL Channel
Constructing a any-1 CALL channel for a specific interface follows exactly the same pattern as in the 1-1 case. Of course, it must extend Any2OneCallChannel rather than One2OneCallChannel.For example, using the same Foo interface as before, we derive:
import org.jcsp.lang.*; public class Any2OneFooChannel extends Any2OneCallChannel implements Foo { ... same body as One2OneFooChannel }
Calling a CALL Channel
All the client needs to see is the method interface implemented by the CALL channel. So far as the client is concerned, therefore, there is no difference between any of the varieties of CALL channel - it just makes the call.Accepting a CALL Channel
The mechanics of accepting a CALL channel are the same for all varieties. However, the server should declare which kind (or kinds) it allows to be attached:import org.jcsp.lang.*; class B implements CSProcess, Foo { private final ChannelAccept in; public B (final One2OneFooChannel in) { // original constructor this.in = in; } public B (final Any2OneFooChannel in) { // additional constructor this.in = in; } ... rest as before }
When wrapping the above to hide its raw method interface, don't forget to include the extra constructor(s):import org.jcsp.lang.*; public class B2 implements CSProcess { // no Foo interface private final B b; public B2 (final One2OneFooChannel in) { // original constructor b = new B (in); } public B2 (final Any2OneFooChannel in) { // additional constructor b = new B (in); } public void run () { b.run (); } }
ALTing on a CALL Channel
The server may ALT on a any-1 CALL channel, just as it may ALT on a 1-1 one. As before, it needs to make its intentions explicit. So, in the above example, the first field declaration of B needs to become:private final AltingChannelAccept in;
See below for an example of ALTing between CALL channels.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 network consisting of one server and several clients:
where A is unchanged from its definition in One2OneCallChannel, is implemented by:Any2OneFooChannel c = new Any2OneFooChannel (); final A[] aClients = new A[n_aClients]; for (int i = 0; i < aClients.length; i++) { aClients[i] = new A (c); } new Parallel ( new CSProcess[] { new Parallel (aClients), new B2 (c) } ).run ();
Example
This is a CALL channel version of the Wot-no-Chickens example:
The service CALL channel replaces the request/deliver channel pair of the earlier example. Previously, the philosopher had to perform two actions to get a chicken - a request.write followed by a deliver.read. Now, its interaction with the canteen is a single CALL on service.takeChicken.The supply CALL channel replaces the ordinary channel of the same name. Previously, the chef still had to perform two actions to supply the chickens - a supply.write followed by a second supply.write. This was to model the extended period while the chef set down the chickens in the canteen. The first communication synchronised the chef with the canteen, getting its exclusive attention. The canteen then executed the set-down delay before accepting the second communication and, hence, releasing the chef. Now, this interaction is a single CALL on supply.freshChickens.
The other difference with the earlier example is that the college now employs many chefs. This has two minor impacts. It needs to be able to support any-1 CALLs on its supply channel (as well as on service). Secondly, with all those chefs, it needs to be able to refuse further supplies of chicken if it has run of room.
The Canteen
There are two other differences in design between the canteen server here and the CALL channel servers documented above and inOne2OneCallChannel
. The first is trivial - we have inlined the real server as an anonymous inner class of the public Canteen wrapper. The second is more subtle, but also trivial. Often, a CALL channel is constructed for a specific server interface and there is no intention for it to be used for communicating with any other server. In which case, it makes sense to tie that interface, together with its corresponding CALL channel, into the server as inner declarations.So, the Canteen first publishes its two specific interfaces and matching CALL channels. The CALL channels follow the defined pattern, omitting the optional setting of selected (since each interface contains only one method):
import org.jcsp.lang.*; class Canteen implements CSProcess { public static interface Service { public int takeChicken (String philId); } public static class One2OneServiceChannel extends One2OneCallChannel implements Service { public int takeChicken (String philId) { join (); int n = ((Service) server).takeChicken (philId); fork (); return n; } } public static class Any2OneServiceChannel extends Any2OneCallChannel implements Service { public int takeChicken (String philId) { join (); int n = ((Service) server).takeChicken (philId); fork (); return n; } } public static interface Supply { public int freshChickens (String chefId, int value); } public static class Any2OneSupplyChannel extends Any2OneCallChannel implements Supply { public int freshChickens (String chefId, int value) { join (); int n = ((Supply) server).freshChickens (chefId, value); fork (); return n; } }
Note that we have defined both 1-1 and any-1 versions of the Service CALL channel. This example makes use only of the any-1 variant - the other will be used in a later exercise.Next we set up the constructor and the local fields for saving its parameters:
private final AltingChannelAccept service; // shared from all Philosphers private final AltingChannelAccept supply; // shared from all Chefs private final int serviceTime; // how long a philosopher spends in the canteen private final int supplyTime; // how long a chef spends in the canteen private final int maxChickens; // maximum number of chickens in the canteen public Canteen (Any2OneServiceChannel service, Any2OneSupplyChannel supply, int serviceTime, int supplyTime, int maxChickens) { this.service = service; this.supply = supply; this.serviceTime = serviceTime; this.supplyTime = supplyTime; this.maxChickens = maxChickens; }
Now, we need to combine the exported interfaces into a single one so that the inner process can be created (anonymously) by this wrapper's run method:private interface inner extends CSProcess, Service, Supply {}; public void run () { new inner () { private int nChickens = 0; private int nSupplied = 0; private final CSTimer tim = new CSTimer ();
Impementations of the required CALL interfaces come next:public int takeChicken (String philId) { // pre : nChickens > 0 System.out.println (" Canteen -> " + philId + " : one chicken ordered ... " + nChickens + " left ... "); tim.sleep (serviceTime); nChickens--; nSupplied++; System.out.println (" Canteen -> " + philId + " : one chicken coming down ... " + nChickens + " left ... [" + nSupplied + " supplied]"); return 1; } public int freshChickens (String chefId, int value) { // pre : nChickens < maxChickens System.out.println (" Canteen <- " + chefId + " : ouch ... make room ... "); tim.sleep (supplyTime); nChickens += value; int sendBack = nChickens - maxChickens; if (sendBack > 0) { nChickens = maxChickens; System.out.println (" Canteen <- " + chefId + " : full up ... sending back " + sendBack); } else { sendBack = 0; } System.out.println (" Canteen <- " + chefId + " : more chickens ... " + nChickens + " now available ... "); return sendBack; }
and the run method that conducts everything:public void run () { final Alternative alt = new Alternative (new Guard[] {supply, service}); final boolean[] precondition = {true, false}; final int SUPPLY = 0; final int SERVICE = 1; System.out.println (" Canteen : starting ... "); while (true) { precondition[SERVICE] = (nChickens > 0); precondition[SUPPLY] = (nChickens < maxChickens); switch (alt.fairSelect (precondition)) { case SUPPLY: supply.accept (this); // new batch of chickens from a chef break; case SERVICE: service.accept (this); // a philosopher wants a chicken break; } } }
Finally, don't forget to run this inner process:}.run (); } }
The Philosopher
As in the original example, philosophers spend their time thinking, feeling hungry, calling on the canteen and, once served, eating. Except, of course, for greedy philosophers who never stop to think:import org.jcsp.lang.*; class Phil implements CSProcess { private final String id; private final Canteen.Service service; private final int thinkTime; private final int eatTime; private final boolean greedy; public Phil (String id, Canteen.Service service, int thinkTime, int eatTime, boolean greedy) { this.id = id; this.service = service; this.thinkTime = thinkTime; this.eatTime = eatTime; this.greedy = greedy; } public void run () { final CSTimer tim = new CSTimer (); int nEaten = 0; while (true) { if (! greedy) { System.out.println (" Phil " + id + " : thinking ... "); tim.sleep (thinkTime); // thinking } System.out.println (" Phil " + id + " : gotta eat ... "); int chicken = service.takeChicken (id); nEaten++; System.out.println (" Phil " + id + " : mmm ... that's good [" + nEaten + " so far]"); tim.sleep (eatTime); // eating } } }
The Chef
Chefs cook chickens in batches of batchSize, taking batchTime milliseconds per batch. When a batch is ready, the chef supplies it to the canteen. The chef has to wait until the canteen is prepared to take it and, then, helps to set down the batch (before returning with any for which there was no space) - all this happens during the CALL of supply.freshChickens:import org.jcsp.lang.*; class Chef implements CSProcess { private final String id; private final int batchSize; private final int batchTime; private final Canteen.Supply supply; public Chef (String id, int batchSize, int batchTime, Canteen.Supply supply) { this.id = id; this.batchSize = batchSize; this.batchTime = batchTime; this.supply = supply; } public void run () { final CSTimer tim = new CSTimer (); int nReturned = 0; int nSupplied = 0; while (true) { System.out.println (" Chef " + id + " : cooking ... " + (batchSize - nReturned) + " chickens"); tim.sleep (batchTime); System.out.println (" Chef " + id + " : " + batchSize + " chickens, ready-to-go ... "); nReturned = supply.freshChickens (id, batchSize); nSupplied += (batchSize - nReturned); System.out.println (" Chef " + id + " : " + nReturned + " returned [" + nSupplied + " supplied]"); } } }
The Clock
The college is feeling generous and provides a clock. This just ticks away, delivering time-stamps roughly every second (and maintaining real-time). It is independent of the rest of the system.import org.jcsp.lang.*; class Clock implements CSProcess { public void run () { final CSTimer tim = new CSTimer (); final long startTime = tim.read (); while (true) { int tick = (int) (((tim.read () - startTime) + 500)/1000); System.out.println ("[TICK] " + tick); tim.sleep (1000); } } }
The College
Despite the greedy behaviour of philosopher 0 (Bill), nobody starves in this college. Three chefs are provided with differing cooking speeds and batch sizes. Pierre is the original lightning chef, cooking 4 chickens in 2 seconds flat. Henri is more leisurely, taking 20 seconds to cook his batch of 10. Sid has been sent down by the new owners of the college, who are into mass catering. He produces 100 chickens every 150 seconds, which is a bit silly since the canteen has only space for 50. Still, it enables Bill to get really sick!For convenience, the college network diagram is reproduced here - this time including the clock and naming some of the characters:
Here is the code:import org.jcsp.lang.*; class College implements CSProcess { public void run () { final String[] philId = {"Bill", "Hilary", "Gennifer", "Paula", "Monica"}; final int thinkTime = 3000; // 3 seconds final int eatTime = 100; // 100 milliseconds final int serviceTime = 0; // 0 seconds final int supplyTime = 3000; // 3 seconds final int maxChickens = 50; final Canteen.Any2OneServiceChannel service = new Canteen.Any2OneServiceChannel (); final Canteen.Any2OneSupplyChannel supply = new Canteen.Any2OneSupplyChannel (); final Phil[] phils = new Phil[philId.length]; for (int i = 0; i < phils.length; i++) { phils[i] = new Phil (philId[i], service, thinkTime, eatTime, i == 0); } new Parallel ( new CSProcess[] { new Clock (), new Canteen (service, supply, serviceTime, supplyTime, maxChickens), new Parallel (phils), new Chef ("Pierre", 4, 2000, supply), // chefId, batchSize, batchTime new Chef ("Henri", 10, 20000, supply), new Chef ("Sid", 100, 150000, supply) } ).run (); } public static void main (String argv[]) { new College ().run (); } }
A Note about System.out and Other Non-Blocking Monitors
The college network diagram and code hides a key player that quietly coordinates the reporting of all activity - System.out. All the processes share this object as a common resource, making heavy and concurrent demands on its println service. Why is this safe?Consider a Java object whose public methods are all synchronized but contain no invocations of wait or notify (a passive non-blocking monitor). Such an object is equivalent to a CSProcess serving one or more any-1 CALL channels (whose interfaces reflect those synchronized methods) and whose run consists of an endless loop that does nothing except unconditionally accept any CALL.
So, a simple non-blocking monitor is always safe to share between concurrent JCSP processes and, currently, carries less overheads than its active server equivalent. See
DisplayList
for an example from the JCSP library. Another example isjava.io.PrintStream
, of which System.out is an instance. Its print/println methods are synchronized on itself (although this does not seem to be documented and you have to look hard at the code to find out). So, to show the full story, the above diagram possibly needs an overlay that adds a System.out process servicing a any-1 println CALL channel, with all the other processes as clients. This is left as an exercise.Of course, it would be nice if such monitors were accessed via an interface, so that client processes had neither direct visibilty of them nor concern about their behaviour. A problem with the above college is that System.out - and the concept of printing a line of text - is burnt into the code of all its processes. If we wanted to change the output of the college from a scrolling text display into some graphics animation, all those processes would have to be changed.
A better design would pass in channel (or CALL channel or non-blocking monitor) interfaces to each of the college processes. These would merely report their identities and states by writing to (or calling or invoking) those interfaces. To reproduce the current display, all those interfaces would be instanced by a single any-1 channel (or CALL channel or monitor) connected to a simple server that responds by making System.out.println invocations appropriate to the information passed. For other effects, connect in other servers. Note that the college processes do not have to be connected to the same server - each could be connected to a separate server and these servers connected into a graphics animation network (incorporating, for example, processes from
org.jcsp.awt
). The point is that the college processes would need no changing to drive whatever was constructed. This is also left as an exercise.Further Thoughts on the Canteen Design
The decision to bind in the CALL channel (and associated inteface) definitions as inner classes of Canteen does not fit comfortably with the above observations. However, processes servicing Canteen.Any2OneServiceChannel or Canteen.Any2OneSupplyChannel channels do not have to be instances of Canteen - even though that would seem to be a little odd. So, the College authorities still have freedom to install canteens with behaviours quite different to that of Canteen. [Anything with the same channel interface will fit! The interoperability of processes depends only on the compatibility of their channel interfaces. Note that this reusability owes nothing to the concept of inheritance - for example, the alternative canteens pluggable into the college network need no special sub-classing relationships.] So, if we really want to allow this flexibility, make it explicit by declaring the CALL channels separately from any of their servers.Another design choice is to burn in CALL channel instances as part of the servers themselves. For example, the Canteen class could construct and export its service and supply channels as public and final fields (rather than import them via constructor parameters). In this case, the College builder would need to declare and name the canteen (instead of declaring and naming the channels):
import org.jcsp.lang.*; class College implements CSProcess { public void run () { ... declare constants (nPhilosophers, thinkTime etc.) final Canteen canteen = new Canteen (serviceTime, supplyTime, maxChickens); final Phil[] phils = new Phil[nPhilosophers]; for (int i = 0; i < phils.length; i++) { String philId = new Integer (i).toString (); phils[i] = new Phil (philId, canteen.service, thinkTime, eatTime, i == 0); } new Parallel ( new CSProcess[] { new Clock (), canteen, new Parallel (phils), new Chef ("Pierre", 4, 2000, canteen.supply), new Chef ("Henri", 10, 20000, canteen.supply), new Chef ("Sid", 100, 60000, canteen.supply) } ).run (); } ... main }
Note that this particular burn in does not deny any flexibility to the college in choosing any particular variety of canteen. In fact, the only thing of interest to the college is that the canteen provides and services CALL channels whose interfaces are what its philosophers and chefs expect (i.e. Service and Supply). Note also that the college network diagram has not changed.Having gone this far, we may like to consider making the server self-starting - so that its declaration not only introduces its service channels but also brings it to life. For example, this could be done for the Canteen by adding the following as the last line of its constructor:
new
Of course, the canteen instance should then be removed from theProcessManager
(this).start ();Parallel
construction above.[Warning: be careful if sub-classes are allowed (i.e. the Canteen class was not declared final). In this case, the above incantation should be optional so that each sub-class constructor can invoke a super-class constructor that omits it. If we let the super-class fire up the process, it may start running before the sub-class constructor finishes - i.e. before the process has been fully initialised. It must be the sub-class constructor that self-starts the process (as the last thing it does).]
[Note: a self-starting server exporting its own CALL (or ordinary) channels for public concurrent use corresponds to the occam3 notion of a MODULE implemented by a RESOURCE.]
- Author:
- P.H. Welch
- See Also:
One2OneCallChannel
,One2AnyCallChannel
,Any2AnyCallChannel
,Alternative
, Serialized Form
-
-
Field Summary
Fields Modifier and Type Field Description 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
Constructors Constructor Description Any2OneCallChannel()
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description 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.
-
-
-
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 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 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 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
.
-
-
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 fromOne2OneCallChannel
. 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.
-
-