All this said, I have just spent the past two hours implementing
something very much like this in a Java framework for executing
scripts in an embedded Rhino engine.

The functionality makes these assumptions:
 + scripts are always single-threaded.
 + scripts share no locks or lockable objects with other threads.
 + scripts does not do any IO on shared resources.

Due to the nature of the context in which this framework operates,
these assumptions are considered safe.

The goal of the functionality I implemented is to guarentee that
scripts will always terminate within a given timeframe. This guarentee
is upheld to the best of effort, with an expected precision of about
300 ms under normal load (in this particular environment).

Here's the high-level view of how it works (if you spot any errors in
this logic, then I will be grateful for the input):

We have two kinds of threads:
 + A watcher thread; singleton, daemon. Responsible for overseeing
execution time of other threads, and kill them if they take too long.
 + Normal threads; goes about their business, and some times run scripts.

The single instance of the watcher thread (which is created and
started in a static { ... } block) sits in a ScriptHandler class. This
class also has a static final
java.util.concurrent.ConcurrentLinkedQueue<ScriptExecution> instance,
that the watcher thread can access (more about this below).

When a Normal Thread wants to run a script, this is what happens:
 + call run(aScript) (which is static) on a ScriptHandler, which in turn...
 + ... call watcher.enter() - this call yields an ExecutionToken (more
on this below).
 + ... wrapped in 'try', run the actual script.
 + ... then in finally, call watcher.exit(theToken).

Pretty simple so far. Before going on about what the watcher does,
here's a description of the classes and objects mentioned:

java.util.concurrent.ConcurrentLinkedQueue class:
A wait-free thread-safe queue where the head is always the oldest item.

ScriptHandler class:
The entry-point class for executing scripts with guarenteed timely
termination. The run() method may throw a ScriptTimeoutException.

ScriptExecution class (the things in the ConcurrentLinkedQueue):
A container class that wrappes the context of an execution.
Specifically, it contains:
 + a reference to the thread performing the execution.
 + a long containing the System.currentTimeStamp() for when said
thread entered the queue - so we know how old it is.
 + a reference to the ExecutionToken that watcher.enter() gave the above thread.

ExecutionToken class:
A synchronization aid that wraps a java.util.concurrent.Exchanger<?>.
It has three methods:
 + sync(): set 'waiting' to true and block until another thread calls
a sync method, or the thread is interrupted. See:
http://java.sun.com/javase/6/docs/api/java/util/concurrent/Exchanger.html#exchange(V)
 + sync(int ms): set 'waiting' to true and block until another thread
calls a sync method, or the thread is interrupted, or the specified
waiting time runs out. See:
http://java.sun.com/javase/6/docs/api/java/util/concurrent/Exchanger.html#exchange(V,
long, java.util.concurrent.TimeUnit)
 + isWaiting(): return 'true' if a thread is waiting to sync with a peer.

ScriptTimeoutException class:
The RuntimeException that is used to indicate that a script has been
terminated because it took too long. In Rhino, JavaScript is not
allowed to catch RuntimeExceptions, so it is guarenteed to propergate
back out of Rhino and kill the script.

These are the basic building blocks of the solution, and here's how
the watcher uses them:

In an infinite loop the watcher does the following:
 + if queue is empty: sleep & continue
 + otherwise, with head of queue:
 + ... if thread is dead? remove & continue
 + ... if token.isWaiting()? sync(50), remove & continue
 + ... if thread is too old? try:
 + ... ... sync(50), remove & continue
 + ... ... otherwise, on TimeoutException:
 + ... ... ... thread.stop(new ScriptTimeoutException())
 + ... ... ... remove & continue
 + ... otherwise, when thread is not too old:
 + ... ... sleep & continue

Two things to note:
 + watcher ALWAYS calls sync(someNumberOfMilliseconds) because it must
NEVER engage a blocking call that is not guarenteed to return at some
point.
 + Normal Threads ALWAYS calls sync() and patiently wait for the
watcher to pair up.

The fundamental principle for this solution is this:
Users of the ScriptHandler are aware that a ScriptTimeoutException
might be thrown, and have (hopefully) prepared a try-catch for it.
Since the script run in-thread, the watcher MUST NOT call stop() on
threads that are no longer executing any scripts.

In other words:
The watcher must NEVER terminate threads that does not run inside the
scope of the above try-catch.

So that's the short of it, in one statement: don't kill unprepared threads.

I hope it makes sense.

On 4/10/08, Colin Walters <[EMAIL PROTECTED]> wrote:
>
>  On Apr 10, 8:28 am, "Christian Vest Hansen" <[EMAIL PROTECTED]>
>  wrote:
>
> >
>  > Interrupting a thread does not, to the best of my knowledge, guarentee
>  > that an arbitrary body of code will actually stop running. If the code
>  > does not invoke any method that might throw some InterruptedException,
>  > then the onus is on the programmer (of said body of code) to check
>  > what Thread.interrupted() says at regular intervals.
>
>
> Ah, makes sense.  But I guess I'm having trouble thinking of
>  legitimate uses of WITH-TIMEOUT that don't end up blocking on an
>  interruptable method.  If the arbitrary code in the block is itself an
>  interpreter for another kind of arbitrary code from somewhere else
>  (for example, you're using WITH-TIMEOUT around the evaluation of an
>  untrusted regular expression), you probably want strict control over
>  other things such as heap used too.  Which leads me to think that this
>  is a use case for something like MVM: 
> http://java.sun.com/developer/technicalArticles/Programming/mvm/
>  Though I don't know if that's still being actively developed.
>
>  The Emacs Lisp WITH-TIMEOUT implementation seems to have the exact
>  same limitation that the JVM's Thread.interrupt() does.  I'm curious,
>  is this another Lisp platform?
>
> >
>


-- 
Venlig hilsen / Kind regards,
Christian Vest Hansen.

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "JVM 
Languages" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/jvm-languages?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to