160 likes | 285 Views
CSC 580 - Multiprocessor Programming, Spring, 2011. Chapter 7 – Cancellation & Shutdown Dr. Dale E. Parson, week 10. Task cancellation. Activity is cancellable if external code can move it to completion before normal completion. (p. 135) Server or similar non-terminating activity.
E N D
CSC 580 - Multiprocessor Programming, Spring, 2011 Chapter 7 – Cancellation & Shutdown Dr. Dale E. Parson, week 10
Task cancellation • Activity is cancellable if external code can move it to completion before normal completion. (p. 135) • Server or similar non-terminating activity. • Long-running computation. • Cancellable implies asynchronous notification. • User-requested cancellation. • Time-limited activities. • Application events (e.g., first solution stops others). • Errors (e.g., file server backing database goes off line). • Shutdown.
Interruption • Thread.currentThread() gets current thread. • interrupt() interrupts a thread object. • Asynchronous but not preemptive. • isInterrupted() returns some thread’s status without changing it. • Thread.interrupted() returns current thread’s interrupt status and clears it. • InterruptedException thrown by many blocking methods. Thrower may clear interrupted status.
Interruption for cancellation • Calling interrupt() merely delivers a message. • Using interruption for anything but cancellation is fragile and difficult to sustain in larger applications. (p. 138) • Interruption is usually the most sensible way to implement cancellation. (p. 140) • The intent is that threads running application tasks will have periodic opportunities to notice the interruption message, and know what that means.
Cancellation & Interruption Policy • A Cancellation Policy is a plan for how an algorithm quits in the middle of its work. • An Interruption Policy is a plan for how application threads and tasks respond to interrupt messages. • Interruption is ultimately under the control of the application framework. • The design of an application framework must establish how it uses interrupts to communicate with tasks.
Responding to an interrupt • Do not interrupt() a thread unless you know its interruption policy. Interruption typically comes from elsewhere in application code. • Library and application-neutral framework code may neither interpret nor ignore InterruptedException. • A library method may throw the InterruptedException. • A library method may re-call interrupt() on its thread. • Application code must handle the interrupt. • Use the cancellation and interruption policy to direct control. • Swallow InterruptedException onlyif that works with the policies.
Task Cancellation • Tasks run in frameworks, e.g., ExecutorService. • submit() returns a Future; it may be cancel()d. • cancel() has mayInterruptIfRunning param. • The application-level task, if running, must decide what to do with the optional interrupt. • Future.get throws CancellationException or ExecutionException for abnormal termination of its task. Latter carries its original cause.
Uninterruptible I/O • InputStream.read and OutputStream.write do not throw InterruptedException. • Calls may block indefinitely. • close() on the underlying stream from another thread causes IOException, but it may be dangerous. • Sending a “Poison pill” (special sentinel value) into the stream from another thread, if possible, is safer. • java.nio and java.nio.channels.Selectors have related issues, also support for closed-by-interrupt exceptions. See Javadoc.
Uninterruptible lock acquisition • Explicit lock has lockInterruptibly(), but lock() and implicit synchronized locks are uninterruptible. • If there is no cyclic dependency among locks (which leads to deadlock), the thread holding the lock must make progress, possibly via interruption, and release the lock. • Compute-bound tasks or tasks that invoke non-interruptible methods, and that have an interruption policy, may need to poll isInterrupted() periodically.
Stopping a thread-based service • Lifecycle methods of the service are necessary for state transitions and termination. • The thread pool that manages the worker threads decides whether and when to interrupt them. • The application code running in the service threads decides how to respond to interruption (policy). • ExecutorService.shutdown • shutdown() – previously submitted tasks are executed, but no new submissions are accepted. • shutdownNow() attempts to terminate tasks (typically via interrupt()) and returns a list of tasks that were awaiting execution.
Poison pill • Poison pill is a sentinel value that a producer passes to consumer(s) to signal shutdown. • Poison pill(s) must come afterall regular producer application data. • From solution #1 Input.run(): if (myburst.isEmpty()) { int remaining = pktq.decrementAndGetInputs(); if (remaining == 0) { // I was the last Input thread, send a sentinel to each // output thread. int outputcount = pktq.getOutputs(); for (int i = 0 ; i < outputcount ; i++) { pktq.enqueue(myburst); // empty list is sentinel } } return ; }
Limitations of shutdownNow • There is no option to cancel un-started tasks while allowing started tasks to complete. • It is possible to extend or adapt ExecutorService to interact with tasks in an application-specific way to determine which tasks are started without completion, and to stop only the unstarted ones. See 7.2.5. • Race condition at task completion time is still likely.
Abnormal thread termination • Possible to log a stack trace, but general purpose thread pool library classes do not own the application logs. • Printing stack traces to System.err may be lost. • General purpose framework (e.g., Eclipse) may catch unchecked exceptions (RuntimeException) and display a warning about the suspect integrity of the compromised application task or plug-in.
Boilerplate code for dealing with untrusted tasks (p. 162) public void run() { Throwable thrown = null ; try { while (! currentThread().isInterrupted()) { runTask(getTaskFromWorkQueue()); } } catch (Throwable e) { thrown = e ; } finally { threadExited(this, thrown); }}
JVM shutdown and Daemon threads • Runtime.addShutdownHook() deals with cases where a thread invokes System.exit() or a terminating signal arrives from the O.S. • It starts a series of registered threads. • Make these thread-safe, defensive, and conservative. • Daemon threads are never joined to the main thread. Test advises to use them sparing, perform no I/O in them. Use them for housekeeping such as managing an in-memory cache.
Finalizers • Object.finalize() runs when the garbage collector is about to recover an object. • Test says: “Avoid finalizers.” • Explicit try-finally blocks that close() (and flush) output resources are safer and easier than using finalizers. • They may be necessary for freeing resources obtained by native methods.