Changeset 31707


Ignore:
Timestamp:
2017-05-26T19:43:28+12:00 (7 years ago)
Author:
ak19
Message:

Adding an option to cancelProcess() that allows you to force the caller, even if it's a GUI thread, to wait until the cancelProcess() method's end (which ends when the SafeProcess becomes interruptible). There's now a default cancelProcess() again, that takes no arguments, and this is what we call. It behaves as before: if called from a GUI thread, it won't wait for the SafeProcess to become interruptible.

Location:
main/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/gli/src/org/greenstone/gatherer/util/SafeProcess.java

    r31706 r31707  
    4343    public static String WIN_KILL_CMD;
    4444
    45     /**
    46     * Boolean interruptible is used to mark any sections of blocking code that should not be interrupted
    47     * with an InterruptedExceptions. At present only the cancelRunningProcess() attempts to do such a thing
    48     * and avoids doing so when interruptible is false.
    49     * Note that interruptible is also used as a lock, so remember to synchronize on it when using it!
    50     */
     45    /**
     46    * Boolean interruptible is used to mark any sections of blocking code that should not be interrupted
     47    * with an InterruptedExceptions. At present only the cancelRunningProcess() attempts to do such a thing
     48    * and avoids doing so when interruptible is false.
     49    * Note that interruptible is also used as a lock, so remember to synchronize on it when using it!
     50     */
    5151    public Boolean interruptible = Boolean.TRUE;
    5252   
     
    7676    // allow callers to process exceptions of the main process thread if they want
    7777    private ExceptionHandler exceptionHandler = null;
    78     /** allow callers to implement hooks that get called during the main phases of the internal
    79     * process' life cycle, such as before and after process.destroy() gets called
    80     */
     78    /** allow callers to implement hooks that get called during the main phases of the internal
     79    * process' life cycle, such as before and after process.destroy() gets called
     80     */
    8181    private MainProcessHandler mainHandler = null;
    8282
     
    127127
    128128    /** to set a handler that will handle the main (SafeProcess) thread,
    129     * implementing the hooks that will get called during the internal process' life cycle,
     129    * implementing the hooks that will get called during the internal process' life cycle,
    130130     * such as before and after process.destroy() is called */
    131131    public void setMainHandler(MainProcessHandler handler) {
     
    153153
    154154    /**
    155      * Call this method when you want to prematurely and safely terminate any process
    156      * that SafeProcess may be running.
    157      * You may want to implement the SafeProcess.MainHandler interface to write code
    158      * for any hooks that will get called during the process' life cycle.
    159      * @return false if process has already terminated or if it was already terminating
    160      * when cancel was called. In such cases no interrupt is sent. Returns boolean sentInterrupt.
     155     * If calling this method from a GUI thread when the SafeProcess is in the uninterruptible
     156     * phase of natural termination, then this method will return immediately before that phase
     157     * has ended. To force the caller to wait until the natural termination phase has ended,
     158     * call the other variant of this method with param forceWaitUntilInterruptible set to true:
     159     * cancelRunningProcess(true).
     160     * @return false if process has already terminated or if it was already terminating when
     161     * cancel was called. In such cases no interrupt is sent.
     162     * This method returns a boolean that you can call sentInterrupt. 
    161163     */
    162     public synchronized boolean cancelRunningProcess() {
     164    public boolean cancelRunningProcess() {
     165
     166    boolean forceWaitUntilInterruptible = true;
     167    // by default, event dispatch threads may not want to wait for any joins() taking
     168    // place at the time of cancel to be completed.
     169    // So don't wait until the SafeProcess becomes interruptible
     170    return this.cancelRunningProcess(!forceWaitUntilInterruptible);
     171    }
     172
     173    /**
     174     * Call this method when you want to prematurely and safely terminate any process
     175     * that SafeProcess may be running.
     176     * You may want to implement the SafeProcess.MainHandler interface to write code
     177     * for any hooks that will get called during the process' life cycle.
     178     * @param forceWaitUntilInterruptible if set to true by a calling GUI thread, then this method
     179     * won't return until the running process is interruptible, even if SafeProcess is in the phase
     180     * of naturally terminating, upon which no interrupts will be sent to the SafeProcess
     181     * thread anyway. The join() calls within SafeProcess.runProcess() are blocking calls and are
     182     * therefore sensitive to InterruptedExceptions. But the join() calls are part of the cleanup
     183     * phase and shouldn't be interrupted, and nothing thereafter can be interrupted anyway.
     184     * This method tends to be called with the param set to false. In that case, if the SafeProcess
     185     * is in an uninterruptible phase (as can then only happen during clean up of natural
     186     * termination) then a calling GUI thread will just return immediately. Meaning, the GUI thread
     187     * won't wait for the SafeProcess thread to finish cleaning up.
     188     * @return false if process has already terminated or if it was already terminating when
     189     * cancel was called. In such cases no interrupt is sent.
     190     * This method returns a boolean that you can call sentInterrupt.
     191     */
     192    public synchronized boolean cancelRunningProcess(boolean forceWaitUntilInterruptible) {
    163193    // on interrupt:
    164194    // - forciblyTerminate will be changed to true if the interrupt came in when the process was
     
    182212    // have to wait until afterward     
    183213    if (interruptible) {
    184         // either way, we can now interrupt the thread - if we have one (we should)
    185         if(this.theProcessThread != null) { // we're told which thread should be interrupted
     214        // either way, we can now interrupt the thread that SafeProcess.runProcess() is running in
     215        if(this.theProcessThread != null) { // we stored a ref to the main thread that's to be interrupted
    186216        this.theProcessThread.interrupt();
    187217        log("@@@ Successfully sent interrupt to process.");
     
    189219        }
    190220    }
    191     else { // wait for join()s to finish.
     221    else { // wait for join()s to finish, if we've been asked to wait
     222       
    192223        // During and after joining(), there's no need to interrupt any more anyway: no calls
    193         // subsequent to joins() block, so everything thereafter is insensitive to InterruptedExceptions.
    194 
    195         if(SwingUtilities.isEventDispatchThread()) {
     224        // subsequent to joins() block, so everything thereafter is insensitive to InterruptedExceptions
     225        // and everything from the joins() onward are cleanup on natural process termination, so no
     226        // interrupt is needed after the joins().
     227        // Still, even if the caller is a GUI thread, they can decide if they want to wait until this
     228        // method's end: until the SafeProcess becomes interruptible again
     229
     230        if(!forceWaitUntilInterruptible && SwingUtilities.isEventDispatchThread()) {
    196231        log("#### Event Dispatch thread, returning");
    197232        return false;
     
    757792// but the other suggestion did work: pkill -TERM -P pid did work
    758793// More reading:
     794// https://superuser.com/questions/343031/sigterm-with-a-keyboard-shortcut
     795// Ctrl-C sends a SIGNINT, not SIGTERM or SIGKILL. And on Ctrl-C, "the signal is sent to the foreground *process group*."
    759796// https://linux.die.net/man/1/kill (manual)
    760797// https://unix.stackexchange.com/questions/117227/why-pidof-and-pgrep-are-behaving-differently
     
    765802
    766803/**
    767  * On Unix, will kill the process denoted by processID and any subprocessed this launched. Tested on a Mac, where this is used.
     804 * On Unix, will kill the process denoted by processID and any subprocesses this launched. Tested on a Mac, where this is used.
    768805 * @param force if true will send the -KILL (-9) signal, which may result in abrupt termination without cleanup
    769806 * if false, will send the -TERM (-15) signal, which will allow cleanup before termination. Sending a SIGTERM is preferred.
     
    810847    // the process and its subprocesses (don't need to call this method at all to terminate the processes: the processes
    811848    // aren't running when we get to this method)
    812     log("@@@ Sending termination signal returned exit value 1. On unix this happens when the process has already been terminated.");
     849    log("@@@ Sending termination signal returned exit value 1. On unix this can happen when the process has already been terminated.");
    813850    return true;
    814851    } else {
  • main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java

    r31706 r31707  
    4343    public static String WIN_KILL_CMD;
    4444
    45     /**
    46     * Boolean interruptible is used to mark any sections of blocking code that should not be interrupted
    47     * with an InterruptedExceptions. At present only the cancelRunningProcess() attempts to do such a thing
    48     * and avoids doing so when interruptible is false.
    49     * Note that interruptible is also used as a lock, so remember to synchronize on it when using it!
    50     */
     45    /**
     46    * Boolean interruptible is used to mark any sections of blocking code that should not be interrupted
     47    * with an InterruptedExceptions. At present only the cancelRunningProcess() attempts to do such a thing
     48    * and avoids doing so when interruptible is false.
     49    * Note that interruptible is also used as a lock, so remember to synchronize on it when using it!
     50     */
    5151    public Boolean interruptible = Boolean.TRUE;
    5252   
     
    7676    // allow callers to process exceptions of the main process thread if they want
    7777    private ExceptionHandler exceptionHandler = null;
    78     /** allow callers to implement hooks that get called during the main phases of the internal
    79     * process' life cycle, such as before and after process.destroy() gets called
    80     */
     78    /** allow callers to implement hooks that get called during the main phases of the internal
     79    * process' life cycle, such as before and after process.destroy() gets called
     80     */
    8181    private MainProcessHandler mainHandler = null;
    8282
     
    127127
    128128    /** to set a handler that will handle the main (SafeProcess) thread,
    129     * implementing the hooks that will get called during the internal process' life cycle,
     129    * implementing the hooks that will get called during the internal process' life cycle,
    130130     * such as before and after process.destroy() is called */
    131131    public void setMainHandler(MainProcessHandler handler) {
     
    152152    */
    153153
     154
    154155    /**
    155      * Call this method when you want to prematurely and safely terminate any process
    156      * that SafeProcess may be running.
    157      * You may want to implement the SafeProcess.MainHandler interface to write code
    158      * for any hooks that will get called during the process' life cycle.
    159      * @return false if process has already terminated or if it was already terminating
    160      * when cancel was called. In such cases no interrupt is sent. Returns boolean sentInterrupt.
     156     * If calling this method from a GUI thread when the SafeProcess is in the uninterruptible
     157     * phase of natural termination, then this method will return immediately before that phase
     158     * has ended. To force the caller to wait until the natural termination phase has ended,
     159     * call the other variant of this method with param forceWaitUntilInterruptible set to true:
     160     * cancelRunningProcess(true).
     161     * @return false if process has already terminated or if it was already terminating when
     162     * cancel was called. In such cases no interrupt is sent.
     163     * This method returns a boolean that you can call sentInterrupt. 
    161164     */
    162     public synchronized boolean cancelRunningProcess() {
     165    public boolean cancelRunningProcess() {
     166
     167    boolean forceWaitUntilInterruptible = true;
     168    // by default, event dispatch threads may not want to wait for any joins() taking
     169    // place at the time of cancel to be completed.
     170    // So don't wait until the SafeProcess becomes interruptible
     171    return this.cancelRunningProcess(!forceWaitUntilInterruptible);
     172    }
     173
     174    /**
     175     * Call this method when you want to prematurely and safely terminate any process
     176     * that SafeProcess may be running.
     177     * You may want to implement the SafeProcess.MainHandler interface to write code
     178     * for any hooks that will get called during the process' life cycle.
     179     * @param forceWaitUntilInterruptible if set to true by a calling GUI thread, then this method
     180     * won't return until the running process is interruptible, even if SafeProcess is in the phase
     181     * of naturally terminating, upon which no interrupts will be sent to the SafeProcess
     182     * thread anyway. The join() calls within SafeProcess.runProcess() are blocking calls and are
     183     * therefore sensitive to InterruptedExceptions. But the join() calls are part of the cleanup
     184     * phase and shouldn't be interrupted, and nothing thereafter can be interrupted anyway.
     185     * This method tends to be called with the param set to false. In that case, if the SafeProcess
     186     * is in an uninterruptible phase (as can then only happen during clean up of natural
     187     * termination) then a calling GUI thread will just return immediately. Meaning, the GUI thread
     188     * won't wait for the SafeProcess thread to finish cleaning up.
     189     * @return false if process has already terminated or if it was already terminating when
     190     * cancel was called. In such cases no interrupt is sent.
     191     * This method returns a boolean that you can call sentInterrupt.
     192     */
     193    public synchronized boolean cancelRunningProcess(boolean forceWaitUntilInterruptible) {
    163194    // on interrupt:
    164195    // - forciblyTerminate will be changed to true if the interrupt came in when the process was
     
    182213    // have to wait until afterward     
    183214    if (interruptible) {
    184         // either way, we can now interrupt the thread - if we have one (we should)
    185         if(this.theProcessThread != null) { // we're told which thread should be interrupted
     215        // either way, we can now interrupt the thread that SafeProcess.runProcess() is running in
     216        if(this.theProcessThread != null) { // we stored a ref to the main thread that's to be interrupted
    186217        this.theProcessThread.interrupt();
    187218        log("@@@ Successfully sent interrupt to process.");
     
    189220        }
    190221    }
    191     else { // wait for join()s to finish.
     222    else { // wait for join()s to finish, if we've been asked to wait
     223       
    192224        // During and after joining(), there's no need to interrupt any more anyway: no calls
    193         // subsequent to joins() block, so everything thereafter is insensitive to InterruptedExceptions.
    194 
    195         if(SwingUtilities.isEventDispatchThread()) {
     225        // subsequent to joins() block, so everything thereafter is insensitive to InterruptedExceptions
     226        // and everything from the joins() onward are cleanup on natural process termination, so no
     227        // interrupt is needed after the joins().
     228        // Still, even if the caller is a GUI thread, they can decide if they want to wait until this
     229        // method's end: until the SafeProcess becomes interruptible again
     230
     231        if(!forceWaitUntilInterruptible && SwingUtilities.isEventDispatchThread()) {
    196232        log("#### Event Dispatch thread, returning");
    197233        return false;
     
    757793// but the other suggestion did work: pkill -TERM -P pid did work
    758794// More reading:
     795// https://superuser.com/questions/343031/sigterm-with-a-keyboard-shortcut
     796// Ctrl-C sends a SIGNINT, not SIGTERM or SIGKILL. And on Ctrl-C, "the signal is sent to the foreground *process group*."
    759797// https://linux.die.net/man/1/kill (manual)
    760798// https://unix.stackexchange.com/questions/117227/why-pidof-and-pgrep-are-behaving-differently
     
    765803
    766804/**
    767  * On Unix, will kill the process denoted by processID and any subprocessed this launched. Tested on a Mac, where this is used.
     805 * On Unix, will kill the process denoted by processID and any subprocesses this launched. Tested on a Mac, where this is used.
    768806 * @param force if true will send the -KILL (-9) signal, which may result in abrupt termination without cleanup
    769807 * if false, will send the -TERM (-15) signal, which will allow cleanup before termination. Sending a SIGTERM is preferred.
     
    810848    // the process and its subprocesses (don't need to call this method at all to terminate the processes: the processes
    811849    // aren't running when we get to this method)
    812     log("@@@ Sending termination signal returned exit value 1. On unix this happens when the process has already been terminated.");
     850    log("@@@ Sending termination signal returned exit value 1. On unix this can happen when the process has already been terminated.");
    813851    return true;
    814852    } else {
Note: See TracChangeset for help on using the changeset viewer.