|
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.ProcessManager
This enables a CSProcess
to be spawned
concurrently with the process doing the spawning.
Shortcut to the Constructor and Method Summaries.
CSProcess
to be spawned
concurrently with the process doing the spawning. The class provides
methods to manage the spawned process: start
,
suspend
, resume
,
join
and stop
. The spawned process may, of course,
be a Parallel
network of processes to any depth of nesting, in which
case the whole network comes under this management.
Spawning processes is not the normal way of creating a network in JCSP - the
normal method is to use the Parallel
class. However, when we need
to add processes in response to some run-time event, this capability is very
useful.
Note that the methods in ProcessManager mirror those in
java.lang.Thread
(or, more accurately, those in
java.lang.ThreadGroup
). Those java.lang classes
deprecate their suspend, resume and stop
methods because of race-hazard and deadlock problems. We must be aware
of the same dangers that await their careless use
- see the warning below.
Some security managers (e.g. in certain web browsers supporting applets)
will not allow our current implementation of these methods (which uses
ThreadGroups) to work
- see the implementation note below.
The stopOK()
method lets us know if that is the case.
For completeness, ProcessManager is itself a CSProcess
- run
ning a ProcessManager simply runs the process
it is managing.
import jcsp.lang.*; public class ProcessManagerExample1 { public static void main (String[] argv) { final ProcessManager manager = new ProcessManager ( new CSProcess () { public void run () { final CSTimer tim = new CSTimer (); long timeout = tim.read (); int count = 0; while (true) { System.out.println (count + " :-) managed process running ..."); count++; timeout += 100; tim.after (timeout); // every 1/10th of a second ... } } } ); final CSTimer tim = new CSTimer (); long timeout = tim.read (); System.out.println ("\n\n\t\t\t\t\t*** start the managed process"); manager.start (); for (int i = 0; i < 10; i++) { System.out.println ("\n\n\t\t\t\t\t*** I'm still executing as well"); timeout += 1000; tim.after (timeout); // every second ... } System.out.println ("\n\n\t\t\t\t\t*** I'm finishing now!"); } }
import jcsp.lang.*; public class ProcessManagerExample2 { public static void main (String[] argv) { final ProcessManager manager = new ProcessManager ( new CSProcess () { public void run () { final CSTimer tim = new CSTimer (); long timeout = tim.read (); int count = 0; while (true) { System.out.println (count + " :-) managed process running ..."); count++; timeout += 100; tim.after (timeout); // every 1/10th of a second ... } } } ); System.out.println ("\n\n\t\t\t\t\t*** start the managed process"); manager.start (); final CSTimer tim = new CSTimer (); long timeout = tim.read (); for (int i = 0; i < 5; i++) { timeout += 2000; tim.after (timeout); // after 2 seconds ... System.out.println ( "\n\n\t\t\t\t\t*** suspend the managed process (" + i + "/" + 4 + ")" ); manager.suspend (); timeout += 2000; tim.after (timeout); // after 2 more seconds ... manager.resume (); } timeout += 2000; tim.after (timeout); // after 2 more seconds ... System.out.println ("\n\n\t\t\t\t\t*** stop the managed process"); manager.stop (); System.out.println ("\n\n\t\t\t\t\t*** and finish myself!"); } }In this example, the stop is redundant since the manager thread is about to terminate. In general, however, that thread may have other things to do.
Whenever we work with a component of a parallel system, we need to know not just what computations it performs, but also how it interacts with the other components in that system. In CSP, processes interact by synchronising on shared events (such as channel communication) and one of its main achievements is a mathematically precise way of specifying that behaviour.
In the standard Java thread/monitor model, synchronisation is achieved through the invocation of synchronized methods (or blocks) and through the wait/notify mechanisms inherited from the Object class. However, such synchronisations are rarely spelt out in the documentation of classes that indulge in it. Reinforcing this, the javadoc tool in the latest JDK (version 1.2) has dropped the automatic documenting of synchronized from methods so declared. This has a reasonable excuse, since javadoc never reported any synchronized blocks in the body of a method, nor any synchronisations resulting from object methods invoked during method execution. All this information is needed by the user of a component of a parallel system and ought to be included in its formal, or natural language, specification. Failure to keep on top of this causes problems (often impossibly intermittent) of deadlock, livelock, process starvation and/or race-hazard.
. . . System.out.println ( "\n\n\t\t\t\t\t*** suspend the managed process (" + i + "/" + 4 + ")" ); manager.suspend (); timeout += 2000; tim.after (timeout); // after 2 more seconds ... System.out.println ( "\n\n\t\t\t\t\t*** resume the managed process (" + i + "/" + 4 + ")" ); manager.resume (); . . .the program would possibly, although not certainly, deadlock the first time that the new print statement tried to execute. If we were lucky, we would get through the whole sequence of 5 suspensions/resumptions successfully. But deadlock would now pose an ever present threat to each run.
The problem is that the println methods of java.io.PrintStream
synchronise on the PrintStream object -
in this case, System.out. The managed process is in a printing loop and,
therefore, continually laying claim to the monitor lock of System.out.
If the manager process suspends the managed one whilst it has this lock
(a definite possibility with the above code), then the manager had better not try
any printing until resuming that managed process. Otherwise, the manager will block
as it also tries to acquire the System.out lock and will be in no position
to resume the managed process - which is the only way that the lock can get
released. Deadlock!
The real problem is that println does not document its synchronisation
on System.out. Although this is fairly obvious given what it has to do,
it is not a danger that springs to mind when adding a print statement!
To discover this synchronisation, we have to browse its source code (which is
not something we always want or are able to do). [See also
the note
in Any2OneCallChannel
.]
So, care must be taken when suspending a process. Obviously we must not attempt to interact directly with a suspended process (e.g. by a committed input or output to it over a channel). Less obviously, we must not interact with any resource that a suspended process may have acquired. To be aware of such pitfalls, a process should document all such interactions. Otherwise, we end up with examples where adding a print statement renders a system liable to deadlock.
Once we know about such interactions, action can be taken to guarantee deadlock-freedom. In the above case, all we need do is acquire and hold the System.out before suspending the managed process:
synchronize (System.out) { manager.suspend (); }Now, the suspension will only take place when the managed process does not have the lock. Any added print statements are now safe.
To avoid this, a managed process should only be stopped if we know it is in a state where it is not interacting with its environment - or, as in the above example, we do not care about such spoilt data. To know that it is in a stoppable state, the manager ought to listen for a message (e.g. channel communication) from the process that it is ready to be stopped. [An example of such cooperation between managed and manager processes is given in the Hamming demonstration (see jcsp-demos.hamming).]
If the managed process is purely serial, there is not much point in the above trick, since it could simply terminate. However, having a manager stop a complex process network means that the network does not have to make its own provision for termination. The latter can add considerable algorithmic complexity, often to the detriment of the clarity of individual process logic.
Stopping a network by setting a global volatile flag that each process polls from time to time (the first suggestion in the documentation of the deprecated stop method in java.lang.Thread) is not, in general, safe. For example, a thread blocked on a monitor wait will remain blocked if the thread that was going to notify it spots the shut-down flag and terminates. The JDK1.2 documentation describes some work-arounds, but they are not simple and depend in part on the application logic.
For JCSP processes, there is a general solution to this [`Graceful Termination and Graceful Resetting', P.H.Welch, Proceedings of OUG-10, pp. 310-317, Ed. A.W.P.Bakkers, IOS Press (Amsterdam), ISBN 90 5199 011 1, April, 1989], based on the careful distribution of poison over the network's normal communication channels. Future versions of JCSP may take account of this.
suspend()
, resume()
, and stop()
methods
will not work. In this case, no exceptions are raised if the methods are
invoked - just nothing happens. Whether this is the case can be discovered
by invoking the stopOK()
method.
This may have an impact in the simple management control implemented for the
ActiveApplet
class. However, no problems occur if Sun's
Java Plug-in is used in the web browser.
CSProcess
,
Parallel
,
ActiveApplet
Constructor Summary | |
ProcessManager(CSProcess proc)
|
Method Summary | |
void |
join()
Join the managed process (that is wait for it to terminate). |
void |
resume()
Resume the managed process. |
void |
run()
Run the managed process (that is start it and wait for it to terminate). |
void |
start()
Start the managed process (but keep running ourselves). |
void |
stop()
Stop (permanently) the managed process. |
boolean |
stopOK()
Returns whether suspend() , resume() and
stop() are supported. |
void |
suspend()
Suspend the managed process. |
Methods inherited from class java.lang.Object |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructor Detail |
public ProcessManager(CSProcess proc)
proc
- the CSProcess
to be executed by this ProcessManagerMethod Detail |
public void start()
public void stop()
public void suspend()
public void resume()
public void join()
public boolean stopOK()
suspend()
, resume()
and
stop()
are supported.suspend()
, resume()
and
stop()
are supported.public void run()
run
in interface CSProcess
|
CSP for Java (JCSP) 1.0-rc4 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: INNER | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |