views:

272

answers:

2

I have a Main Program which is running a script on the target device(smart phone) and in a while loop waiting for stdout messages. However in this particular case, some of the heartbeat messages on the stdout could be spaced almost 45secs to a 1minute apart.

something like:

stream = device.runProgram(RESTORE_LOGS, new String[] {});
stream.flush();
String line = stream.readLine();
while (line.compareTo("") != 0) {
    reporter.commentOnJob(jobId, line);
    line = stream.readLine();
}    

So, I want to be a able to start a new interruptible thread after reading line from stdout with a required a sleep window. Upon being able to read a new line, I want to be able to interrupt/stop(having trouble killing the process), handle the newline of stdout text and restart a process.

And it the event I am not able to read a line within the timer window(say 45secs) I want to a way to get out of my while loop either.

I already tried the thread.run, thread.interrupt approach. But having trouble killing and starting a new thread.

Is this the best way out or am I missing something obvious?

+2  A: 

It looks like the implementation of System.in varies considerably across platforms and, in particular, doesn't always offer interruptibility or asynchronous closure.

Here is a workaround that doesn't rely on those features, but at the cost of failing to clean up properly; if input isn't received before the timeout expires, the Consumer thread is left in a blocking read().

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

class InterruptInput
{

  private static final String EOF = new String();

  private final SynchronousQueue<String> pipe = new SynchronousQueue<String>();

  private final BufferedReader input;

  private final long timeout;

  InterruptInput(BufferedReader input, long timeout)
  {
    this.input = input;
    this.timeout = timeout;
  }

  public static void main(String... argv)
    throws Exception
  {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    InterruptInput input = 
      new InterruptInput(in, 5000);
    input.read();
  }

  void read()
    throws InterruptedException
  {
    System.out.println("Enter lines of input (or empty line to terminate):");
    Thread t = new Consumer();
    t.start();
    while (true) {
      String line = pipe.poll(timeout, TimeUnit.MILLISECONDS);
      if (line == EOF)
        break;
      if (line == null) {
        System.out.println("Input timed-out.");
        t.interrupt();
        break;
      }
      System.out.println("[input]: " + line);
    }
  }

  private class Consumer
    extends Thread
  {

    Consumer()
    {
      setDaemon(true);
    }

    @Override
    public void run()
    {
      while (!Thread.interrupted()) {
        String line;
        try {
          line = input.readLine();
        }
        catch (IOException ex) {
          throw new RuntimeException(ex);
        }
        try {
          if ((line == null) || (line.length() == 0)) {
            pipe.put(EOF);
            break;
          }
          else {
            pipe.put(line);
          }
        }
        catch (InterruptedException ex) {
          break;
        }
      }
    }
  }

}
erickson
sorry couldnt explain in the comment window, hence posted my approach as a alternate solution instead. Please take a look!
LambeauLeap
yeah thanks, that works! However, I'm struggling with the case where the TimeOut expires, at which point I want to be able to return from the TimeOut thread(run method) back the read() method, where I want to able to continue rest of the execution.Currently, it seems like it exits after the execution of TimeOut(run method) and the only way seems to be send a empty or NULL line in which case the "break" is activated.
LambeauLeap
@LambeauLeap - What do you mean by "continue the execution"? Like, which line would you want to be executed next? Remember that once the timer expires, no more can be read from the stream (because it is closed) and that's true whether you close an `InputStream` or interrupt an `InterruptibleChannel`.
erickson
LambeauLeap
Yes, it will terminate the loop, just as entering an empty line would, and print "Done." Is that what you want? If not, what do you want?
erickson
but currently, I dont think it is printing "Done" in the case when the TimeOut expires. It just prints the "Input timed out" line followed by the stacktrace from IOException of closing the "input" and just hangs there waiting for a EMPTY line input to exit the while loop.
LambeauLeap
@LambeauLeap - Hmm, I am running Sun's 1.6.0_20 JVM, and it exits without an error, which surprised me as I actually expected an `IOException`... which is what you are seeing. I will update my answer to show how to terminate the loop smoothly in this case.
erickson
@erickson - Yeah, I've even tried commenting out the whole try block in TimeOut.run() and using "return;" immediately after SOP("Input timed out") to return to the calling function(read()) and executed the SOP("Done"). Been trying a few other approaches to, but no luck in coming back to the Main thread(read()) in the TimeOut expiring case and exiting gracefully without a NULL or EMPTY input line
LambeauLeap
Timeout is a separate thread... it doesn't "return" to `read()` because it's not the *calling* function (there isn't one, in this case, `run()` is the first function on the call stack). `read()` is the method that *started* the thread, but the thread is now independent.
erickson
I am realizing that after all this refactoring and reading up on the Thread API literature. So, wondering if there is no way the program exits gracefully(ie printing the "Done" line) in a situation when the TimeOut thread exits without user intervention(ie., entering a EMPTY line of text). Coz that is bound to be one of the use cases that Im trying to find a solution for!
LambeauLeap
@LambeauLeap - did you run it with my latest (3 hrs ago) change? It should exit "gracefully" now for you. I haven't been able to reproduce the behavior you report, though, so I don't know for sure.
erickson
I noticed the changes you made(catching an IOException when a line cannot be read) and have been using them. BTW FYI, im running on a Ubuntu Linux Java6(/usr/lib/jvm/java-6-sun/jre/bin/java). And even with that here is OUTPUT i get which still needs a ENTER(empty line) to exit the program:[LT74007RXCVK1:~/test/timerInterrupt $] java InterruptInput_V Enter lines of input (or empty line terminate):Input timed-out.<HERE AFTER waiting 10 SECS I key in a ENTER cmd from the KB> Failed to read line: java.io.IOException: Stream closedDone.can that ENTER command be avoided?
LambeauLeap
Are you running in Eclipse? As I said, you cannot run this in Eclipse; it messes up the standard input streams. That's the only way I've been able to reproduce the results you report.
erickson
No, I wish the comment formatting were a lot more clearer, but if you notice my earlier comments, I am cmd line(javac) compiling and executing. I am re-pasting the same again with EndOfLine(EOL) tags added to clarify:[LT74007RXCVK1:~/test/timerInterrupt $] java InterruptInput<EOL> Enter lines of input (or empty line terminate): <EOL>Input timed-out.<EOL><MY COMMENT TO EXPLAIN: Here after waiting 10 SECS or so I key in a ENTER cmd from the KB> <EOL>Failed to read line: java.io.IOException: Stream closed<EOL> Done<EOL>
LambeauLeap
I just retried it on cmd line(obviously) on a MacOS and repeated the same on my Linux box and see the same behavior. Are you seeing anything else?
LambeauLeap
Yes, on Windows, I get "Done." at the end. I will retry on OS X at home.
erickson
Okay, on Mac OS X, I got the result you do. The Unix-y OS's must keep hold a lock on the stream that prevents the "killer" thread from closing the stream while the main thread is block in a `read()`, just like Eclipse does. I'll think about it, but I don't have any other ideas at the moment.
erickson
Thanks for confirming that, may be in the meanwhile I'll work on perfecting the solution based on the ScheduledExecutorService library you mentioned earlier!
LambeauLeap
With this approach, it is blocking on the ""line = in.readLine();"" line of code way before the TimeOut thread expires, meaning no way without a KB input to exit the loop. I wish there was a before way to realtime communicate b/n the read() and the TimeOut() threads, tried the volatile boolean variable approach and a couple others with no luck :(
LambeauLeap
@LambeauLeap - I implemented another approach that isn't ideal from a clean-up standpoint, but might work better on your platform.
erickson
using the SyncQueue is a nice idea, but I'm worried about the no-so ideal clean-up conditions as you mention. In my case I already have my own Java Library which provides a stream from the device(smartphone) which I intend to read. And in the positive test case, where the input eventually ends with a <EMPTY> line, I still poll to read the returnStatusCode from the script running on the device. Now with that being the situation, I'm wondering if using SyncQueues jeopardizes the situation, plus the other cleanup you talk abt not being handled, that would be handled by the by the JAVA GC right?
LambeauLeap
No, because there's a thread running; threads are "root" objects, not subject to garbage collection. The other solutions I posted previously should work for most `InputStream` implements. `System.in` (standard input) seems to have some special issues. Test the previous solution with the actual stream from the smart phone, instead of reading from `System.in`. It is likely to behave differently.
erickson
what if in the case where the timer doesnt get interrupted within the set time window, instead of ""t.interrupt()"", we null the thread with say: ""t = null"" before breaking? Does that take care of the hanging thread, or are you referring to another runaway thread blocking on IO by the time it gets to SOP("Input timed out")??
LambeauLeap
No, assigning `null` to `t` will not do anything. The `read()` method returns shortly after the `interrupt()` currently, and the stack frame to which `t` belongs vanishes, but the `Thread` will keep running, blocked on a read. However, like I said, it all depends on whether the underlying `InputStream` implementation is asynchronously close-able.
erickson
So, i tried out the new approach using SyncQueues on the device and ofcourse it worked for the postive test case where all the input fall within the defined time window. On the negative test case, where the time window expires, it was throwing the RuntimeException(Socket Exception) in the Consumer thread. So i modified the case to check if the timeExpired variable is set, in which case swallow the socketException thrown as RTE. But like you said, it does atleast exit the stream without a blocking read. So I guess i can live with it
LambeauLeap
A: 

that sure seems like a more elegant solution, especially given I haven't used ScheduledExecutorService before. But I'm still struggling to put all the pieces together though! I'm not sure if and when worker invoked for its 45sec countdown? Also, my intention is for such a worker to restart the countdown once it encounters a line of stdout, essentially resetting the countdown to a fresh 45sec window. Does that help clarify.

While I work to incorporate ScheduledExecutorService into my solution, here's the entire sample code i've been using to replicate it using the threads. Lemme know if I you get it to sooner than I can. I am able to invoke a thread on every newline of stdout I come across, but cannot gracefully handle the case when no interruption occurs for the declared time window :( Hope the comments in the code detailed enough to convey my intentions, else pls let me know and I could clarify:

import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.Channels;

public class InterruptInput {   
    static BufferedReader in = new BufferedReader(
            new InputStreamReader(
            Channels.newInputStream(
            (new FileInputStream(FileDescriptor.in)).getChannel())));
    boolean TimeUp = false;

    public static void main(String args[]) {
        try {

            System.out.println("Enter lines of input (user ctrl+Z Enter to terminate):");
            System.out.println("(Input thread will be interrupted in 10 sec.)");
            // interrupt input in 10 sec

            String line = in.readLine();
            while ((line.compareTo("") != 0)) {
            System.out.println("Read line:'"+line+"'");
        TimeOut ti = new TimeOut();
        Thread t = new Thread(ti);
        t.start();
        if((line = in.readLine()) != null) {
            t.interrupt();
            }
            }
        System.out.println("outside the while loop");
        } catch (Exception ex) {
            System.out.println(ex.toString()); // printStackTrace();
        }
    }

    public static class TimeOut extends Thread {
        int sleepTime = 10000;
        private volatile Thread threadToInterrupt;    
        public TimeOut() {
            // interrupt thread that creates this TimeOut.
            threadToInterrupt = Thread.currentThread();
            setDaemon(true);
        }

    public void run() {
        System.out.println("starting a new run of the sleep thread!!");
            try {
                sleep(10000); // wait 10 sec
            } catch(InterruptedException ex) {/*ignore*/
        System.out.println("TimeOut thread interrupted!!");
        //I need to exit here if possible w.out executing
            //everything after the catch block
        }
           //only intend to come here when the 10sec wait passes
           //without interruption. Not sure if its plausible

        System.out.println("went through TimeOut without interruption");
        //TimeUp = true;
        }
    }
}
LambeauLeap
Please check out the update to my answer.
erickson