|
CSP for Java (JCSP) 1.0-rc4 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: INNER | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Object | +--jcsp.lang.Guard | +--jcsp.lang.AltingChannelAccept | +--jcsp.lang.Any2OneCallChannel
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.
One2OneCallChannel
for general information about CALL channels.
Documented here is information specific to this any-1 version.
For example, using the same Foo interface as before, we derive:
import jcsp.lang.*; public class Any2OneFooChannel extends Any2OneCallChannel implements Foo { ... same body as One2OneFooChannel }
import 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 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 (); } }
private final AltingChannelAccept in;See below for an example of ALTing between CALL channels.
Parallel
.
For example, the network consisting of one server and several clients:
_____ _____ _____ | | | | | | | A | | A | ... | A | |_____| |_____| |_____| ______________ | | | c | | ------------------------------------------<->-----------| B2 | Any2OneFooChannel |______________|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 ();
________ ________ ________ | | | | | | | Chef | | Chef | ... | Chef | |________| |________| |________| _______________ | | | supply | | -------------------------------------------------<->--------| | | Canteen | -------------------------------------------------<->--------| | | | | service |_______________| ___|____ ___|____ ___|____ | | | | | | | Phil | | Phil | ... | Phil | |________| |________| |________|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.
One2OneCallChannel
. 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 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 (); } }
import 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 } } }
import 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]"); } } }
import 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); } } }
For convenience, the college network diagram is reproduced here - this time including the clock and naming some of the characters:
_________ | | ___________ ___________ ___________ | Clock | | | | | | | |_________| | Pierre | | Henri | | Sid | |___________| |___________| |___________| _______________ | | | supply | | -------------------------------------------------<->--------| | | Canteen | -------------------------------------------------<->--------| | | | | service |_______________| ___|____ ___|____ ___|____ | | | | | | | Bill | | Hilary | ... | Monica | |________| |________| |________|Here is the code:
import 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), // chefId, batchSize, batchTime new Chef ("Sid", 100, 150000, supply) // chefId, batchSize, batchTime } ).run (); } public static void main (String argv[]) { new College ().run (); } }
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 is java.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 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.
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 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 ProcessManager
(this).start ();
Of course, the canteen instance should then be removed from the 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.]
One2OneCallChannel
,
One2AnyCallChannel
,
Any2AnyCallChannel
,
Alternative
, Serialized FormField 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 | |
Any2OneCallChannel()
|
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 java.lang.Object |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Field Detail |
protected CSProcess server
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.protected int selected
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 |
public Any2OneCallChannel()
Method Detail |
public int accept(CSProcess server)
One2OneCallChannel
.
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.server
- the server process receiving the CALL.protected void join()
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.protected void fork()
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.0-rc4 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: INNER | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |