Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / September 2007

Tip: Looking for answers? Try searching our database.

Adding a timeout to commands by wrapping + thread - suggestions?

Thread view: 
dduck - 31 Aug 2007 12:31 GMT
Hi,

As I know this is something that is notoriously hard to get right, I
thought I would post my naiive implementation here for comments and
advice.

The problem is a common one: We have several actions that all comply
with the same interface (in this example: Command), but it turns out
that some of them may get "stuck" if e.g. a mountpoint goes missing
during execution, which may happen once in a blue moon. Therefore we
want to develop a common facility to monitor any action for excessive
time-to-complete, and either log the problem or kill the action and
report failure up the chain by an Exception.

I came up with this naiive solution:

NB: Stopwatch is a homebrew class that measures time passed since
construction in millis.

/**
* A simple command.
*/
public interface Command {
   void execute();
}

/**
* A thread that runs a command.
*/
public class CommandThread extends Thread {
   private Command cmd;

   public CommandThread(Command cmd) {
       this.cmd = cmd;
   }

   public void run() {
       cmd.execute();
   }
}

/**
* A command that sleeps for some millis.
*/
public class SleepingCmd implements Command {

   private long howLongL;

   public SleepingCmd(Long howLongL) {
       this.howLongL = howLongL;
   }

   public void execute() {
       try {
           Thread.sleep(howLongL);
       } catch (InterruptedException e) {
           throw new RuntimeException("I was interrupted!");
       }
   }
}

public class TimeoutWrappingCmd implements Command {

   private Command wrappedCmd;

   private long timeoutL;

   public TimeoutWrappingCmd(Command wrappedCmd, long timeoutL) {
       this.wrappedCmd = wrappedCmd;
       this.timeoutL = timeoutL;
   }

   public void execute() {
       Thread cmdThread = new CommandThread(wrappedCmd);
       cmdThread.start();
       Stopwatch sw = new Stopwatch();
       while (sw.elapsedMillisL() < timeoutL && cmdThread.isAlive())
{
           try {
               Thread.sleep(100L);
           } catch (InterruptedException e) {
               throw new RuntimeException(
                   "I was interrupted while waiting to poll
encapsulated command");
           }
       }
       if (cmdThread.isAlive()) {
           cmdThread.interrupt();
       }
       try {
           cmdThread.join();
       } catch (InterruptedException e) {
           throw new RuntimeException(
               "I was interrupted while waiting for encapsulated
command to join");
       }
   }

}

Comments?

Sincerely,
 Anders S. Johansen, Royal Danish Library
Lew - 31 Aug 2007 12:35 GMT
> Comments?

Read /Java Concurrency in Practice/ by Brian Goetz.

Among other things, it's chock-full of idioms for the type of thing you're
trying to do.  It will also help you avoid some of the trouble you'll be in.
I'm certainly no expert; this book helps me to realize that.

Signature

Lew

Daniel Pitts - 31 Aug 2007 17:18 GMT
> > Comments?
>
> Read /Java Concurrency in Practice/ by Brian Goetz.

I second that. One of the best books I've ever read on any subject in
Java. Well written, easy to read, and very accurate.

> Among other things, it's chock-full of idioms for the type of thing you're
> trying to do.  It will also help you avoid some of the trouble you'll be in.
> I'm certainly no expert; this book helps me to realize that.
>
> --
> Lew

Also, look into the interface Callable<E>, or Runnable, it does pretty
much what your Command interface does, but its standard. Callable and
Runnable also gives you the option of using Executors, which are
useful for concurrent tasks, and managing timeouts.

Good luck,
Daniel.
dduck - 03 Sep 2007 12:23 GMT
Well, I have refined the example a bit, but have a new problem:
Exceptions from the thread...

If I wrap a command in a Runnable, it might throw a (runtime)
exception. Apparently such exceptions are only reported, but not
passed on to the calling thread.

I can catch them using an implementation of ThreadGroup that
implements uncaughtException, but re-throwing does not propagate to
the main thread either.

The code below outputs:
Caught exception - throwing a new one
Command was executed just fine

...where I had hoped it would instead die with an exception before
printing "Command was executed just fine".

-- code --

public class OnTimeoutExecuteCmd implements Command {

   private Command wrappedCmd;

   private Command onTimeoutCmd;

   private long timeoutL;

   public OnTimeoutExecuteCmd(Command wrappedCmd, Command
onTimeoutCmd, long timeoutL) {
       this.wrappedCmd = wrappedCmd;
       this.onTimeoutCmd = onTimeoutCmd;
       this.timeoutL = timeoutL;
   }

   static class OverrideThreadGroup extends ThreadGroup {

       public OverrideThreadGroup() {
           super("Re-throw RTE");
       }

       public void uncaughtException(Thread t, Throwable e) {
           System.out.println("Caught exception - throwing a new
one");
           throw new RuntimeException("Uncaught exception from
thread", e);
       }

   }

   public void execute() {
       Thread cmdThread = new Thread(new
CommandRunnable(wrappedCmd));
       cmdThread.setUncaughtExceptionHandler(new
OverrideThreadGroup());
       cmdThread.start();
       try {
           cmdThread.join(timeoutL);
       } catch (InterruptedException e) {
           throw new RuntimeException(
           "I was interrupted while waiting for encapsulated command
to "
           + "join or timeout to expire");
       }
       if (cmdThread.isAlive()) {
           onTimeoutCmd.execute();
       }
       try {
           cmdThread.join();
       } catch (InterruptedException e) {
           throw new RuntimeException(
               "I was interrupted while waiting for encapsulated
command to join");
       }
   }

}

public class ThrowingCmd implements Command {

   public void execute() {
       System.out.println("Throwing now");
       throw new RuntimeException("I threw in the towel");

   }

}

public class TestOnTimeout {

   public static void main(String[] args) {
       Command ote = new OnTimeoutExecuteCmd(new ThrowingCmd(), new
HelloCmd(), 800L);
       try {
           ote.execute();
           System.out.println("Command was executed just fine");
       } catch (RuntimeException e) {
           System.err.println("Runtime exception from ote");
       }
   }

}
dduck - 03 Sep 2007 12:33 GMT
Follow-up:

By changing the execute method like so:

   public void execute() {
       Thread cmdThread = new Thread(new
CommandRunnable(wrappedCmd));
       Thread.currentThread().setUncaughtExceptionHandler(new
OverrideThreadGroup());
       cmdThread.start();
       try {
           cmdThread.join(timeoutL);
       } catch (InterruptedException e) {
           throw new RuntimeException(
           "I was interrupted while waiting for encapsulated command
to "
           + "join or timeout to expire");
       }
       if (cmdThread.isAlive()) {
           onTimeoutCmd.execute();
       }
       try {
           cmdThread.join();
       } catch (InterruptedException e) {
           throw new RuntimeException(
               "I was interrupted while waiting for encapsulated
command to join");
       }
   }

...I now get the re-.thrown exception, BUT not instantly. The output
is:

Throwing now
Exception in thread "Thread-0" java.lang.RuntimeException: I threw in
the towel
    at dk.kb.doms.experimental.ThrowingCmd.execute(ThrowingCmd.java:7)
    at dk.kb.doms.experimental.CommandRunnable.run(CommandRunnable.java:
17)
    at java.lang.Thread.run(Thread.java:613)
Command was executed just fine

I.e. the exception is thrown, but stil not in the main thread.

I guess if all I want is a log message when a command has executed
past the timout I should use a TimerTask instead?
Lew - 03 Sep 2007 15:07 GMT
> Follow-up:
...
> I.e. the exception is thrown, but stil not in the main thread.

Read /Java Concurrency in Practice/ by Brian Goetz.

It talks about how to deal with exceptions that a thread throws, so that an
invoking thread can deal with them.

Signature

Lew

Daniel Pitts - 03 Sep 2007 16:56 GMT
> Follow-up:
>
[quoted text clipped - 42 lines]
> I guess if all I want is a log message when a command has executed
> past the timout I should use a TimerTask instead?

Take a look at the Executor class and the java.util.concurrency
packages.

An Executor will return a Future object, which you can get the return
value of your work unit, or the exception that it throws.

Alternatively, you can wrap your whole run method in a try {} catch
(Throwable t) { }, and save the throwable for your main thread to
handle after the fact. I would *not* suggest this approach.
Ingo R. Homann - 03 Sep 2007 12:39 GMT
Hi,

I do not really know what you expect the program to do. But if an
Exception (RuntimeException, Error or whatelse) is thrown in a Thread,
no other Thread will be informed about that (espezially not the Thread
that started the thread in which the exception is thrown). You might
indicate that by a flag or even by exposing the Exception:

class MyRunnable implements Runnable {

  private Exception e;

  public Exception getException() {
    return e;
  }

  public void run() {
    try {
      // do something that might throw an Exception
    } catch(Exception e) {
      this.e=e;
    }
  }

}

calling it like this:

MyRunnable r=new MyRunnable();
Thread t=new Thread(r);
r.start();
r.join();
if(r.getException()) {
  ...
}

Of course, this violates the general contract that every Thread must
care about its own exceptions. (Note that this might sometimes be useful
e.g. in Server-Threads that do not have an own GUI.)

Ciao,
Ingo
Ingo R. Homann - 04 Sep 2007 08:12 GMT
Hi,

note that my code was only an example to show the concept. In real world
applications, of course, you might encapsulate it like this (written in
my newsreader, so untested):

class MyProcess // no not implement Runnable!
{

  private Exception e;

  private Thread t;

  public void start() {
   t=new Thread(new Runnable(){
     public void run() {
      try {
       // do something that might throw an Exception
      } catch(Exception e) {
        this.e=e;
      }
     });
    t.start();
  }

  public void join throws Exception {
    t.join();
    if(e!=null) {
      throw e;
    }
  }

}

Note that this is also only a "proof of concept" and there are many
things missing (such as catching the problem of joining an unstarted
Thread). You may also add many methods like isRunning, interrupt and so on.

Ciao,
Ingo
Lew - 04 Sep 2007 13:28 GMT
> Hi,
>
[quoted text clipped - 33 lines]
> things missing (such as catching the problem of joining an unstarted
> Thread). You may also add many methods like isRunning, interrupt and so on.

Better make 'e' volatile!

Signature

Lew

Lew - 04 Sep 2007 13:34 GMT
> Hi,
>
[quoted text clipped - 33 lines]
> things missing (such as catching the problem of joining an unstarted
> Thread). You may also add many methods like isRunning, interrupt and so on.

For a second I though 'e' needed to be volatile, then I remembered that
Thread.start() and join() handle the synchronization and memory model visibility.

Signature

Lew

Roedy Green - 03 Sep 2007 12:57 GMT
>The problem is a common one: We have several actions that all comply
>with the same interface (in this example: Command), but it turns out
[quoted text clipped - 3 lines]
>time-to-complete, and either log the problem or kill the action and
>report failure up the chain by an Exception.

check out the new java.util.concurrent to see if there is a suitable
tool in there.  See http://mindprod.com/jgloss/queue.html to get you
started.

If there is not, a Timer would be easier that doing it by hand with a
separate thread.  Just have it go off every x seconds, wake up, poke
around to see if all is behaving. See
http://mindprod.com/jgloss/timer.html

Killing a misbehaving thread is deprecated.  You are supposed to just
ask it gently to commit suicide.  However, if it has gone of the
rails, it won't like pay any attention to you.
Signature

Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com



Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.