Cancellation convention

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Cancellation convention

Mike Quilleash
Quick question about cancelling things and the usual way of doing it.
 
Is it the convention to use Thread.interrupt() to do cancellation of a task in another thread?  Presumably the Thread being run must either be calling API code that is interruptible or check itself for the interrupted state regularly.
 
Specifically I'm interested in being able to cancel long running JDBC calls (using PreparedStatement.cancel()).  Would a sensible way be to fork off the PreparedStatement execution in another thread and have the main thread wait for the second thread to complete, but if it is interrupted then call the PreparedStatement.cancel() and throw an exception out to indicate the cancellation succeeded?  Just after some best practice advice.
 
Cheers.
 
Mike.
 

______________________________________________________________________
This email has been scanned by the MessageLabs Email Security System.
For more information please visit http://www.messagelabs.com/email
______________________________________________________________________

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

tpeierls
Is it safe to share PreparedStatement instances between threads? If not, then there's nothing you can do, since you can't have one thread in PreparedStatement.execute() and another in PreparedStatement.cancel().

If you can guarantee thread-safety of PreparedStatement, then you can use a technique described in Section 7.1.6 of Java Concurrency in Practice: Create a Thread with a custom interruption policy by overriding interrupt() to call PreparedStatement.cancel() in addition to super.interrupt () -- be sure to use a finally block for the latter. (Even better would be to use the CancellableTask technique in Section 7.1.7; the particular implementation described there relies on a feature only available in Java 6, but you can work around that.)

--tim

On 6/6/06, Mike Quilleash <[hidden email]> wrote:
Quick question about cancelling things and the usual way of doing it.
 
Is it the convention to use Thread.interrupt() to do cancellation of a task in another thread?  Presumably the Thread being run must either be calling API code that is interruptible or check itself for the interrupted state regularly.
 
Specifically I'm interested in being able to cancel long running JDBC calls (using PreparedStatement.cancel()).  Would a sensible way be to fork off the PreparedStatement execution in another thread and have the main thread wait for the second thread to complete, but if it is interrupted then call the PreparedStatement.cancel() and throw an exception out to indicate the cancellation succeeded?  Just after some best practice advice.
 
Cheers.
 
Mike.
 

______________________________________________________________________
This email has been scanned by the MessageLabs Email Security System.
For more information please visit <a href="http://www.messagelabs.com/email" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)">http://www.messagelabs.com/email
______________________________________________________________________

_______________________________________________
Concurrency-interest mailing list
[hidden email]
<a href="http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)">http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Brian Goetz
In reply to this post by Mike Quilleash
> Is it the convention to use Thread.interrupt() to do cancellation of a
> task in another thread?  Presumably the Thread being run must either be
> calling API code that is interruptible or check itself for the
> interrupted state regularly.

Interrupting a thread when you do not know (a) exactly what code is
running in it and (b) how it will react to interruption is ill-advised.
  Better to use the cancel() feature of Future (to avoid problem (a))
and code your tasks to deal properly with IE (to avoid (b)).

You can then run such tasks in a standard thread pool, which is usually
preferable to forking off a separate thread.

(This material is covered fairly extensively in Ch7 of JCiP.)

> Specifically I'm interested in being able to cancel long running JDBC
> calls (using PreparedStatement.cancel()).  Would a sensible way be to
> fork off the PreparedStatement execution in another thread and have the
> main thread wait for the second thread to complete, but if it is
> interrupted then call the PreparedStatement.cancel() and throw an
> exception out to indicate the cancellation succeeded?  Just after some
> best practice advice.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Brian Goetz
In reply to this post by tpeierls
> Is it safe to share PreparedStatement instances between threads? If not,
> then there's nothing you can do, since you can't have one thread in
> PreparedStatement.execute() and another in PreparedStatement.cancel().

We don't know.  The specification is absolutely silent on the subject of
the thread-safety of JDBC objects.  However, serial thread confinement
can be applied to the entire family of related JDBC objects (Connection
/ PS / ResultSet), and in the absence of a truly whacked out
interpretation of the spec, should probably be OK.

Some vendors try to make their JDBC drivers safe (like Oracle.)
Sometimes they succeed, though the Oracle driver has had well-known
deadlock problems in the past (can't say about hte current version.)

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

tpeierls
On 6/6/06, Brian Goetz <[hidden email]> wrote:
> Is it safe to share PreparedStatement instances between threads?

We don't know.  The specification is absolutely silent on the subject of
the thread-safety of JDBC objects.  However, serial thread confinement
can be applied to the entire family of related JDBC objects (Connection
/ PS / ResultSet), and in the absence of a truly whacked out
interpretation of the spec, should probably be OK.

Mike seemed to be proposing something outside of serial thread confinement: PS.execute in one thread, and PS.cancel in another. But your argument (here and in JCiP 4.5.1) is that providing a cancel method in the API makes no sense if you can't use it in this way.


Some vendors try to make their JDBC drivers safe (like Oracle.)
Sometimes they succeed, though the Oracle driver has had well-known
deadlock problems in the past (can't say about the current version.)

It's quite possible that some driver implementations work by accident.

--tim

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Dhanji R. Prasanna
In reply to this post by Mike Quilleash
On 6/7/06, Mike Quilleash <[hidden email]> wrote:
>

>
> Specifically I'm interested in being able to cancel long running JDBC calls
> (using PreparedStatement.cancel()).

JDBC provides a setQueryTimeout which will throw a SQLException if the
PS runs past the timeout limit?
This should be the preferred way of timing out long-running queries,
as the semantics of concurrency with JDBC are obscure at best--unless
Im very much mistaken and have misunderstood your problem.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Mike Quilleash
In reply to this post by Mike Quilleash
Thanks for your replies guys.

My problem isn't really a timeout problem it's a user cancellation
problem.  Some of our queries can be extremely long running
(minutes-hours) and it would be nice for the user to be able to cancel
them mid-process for whatever reason, they might spot a problem in the
configuration etc.

The problem is when the execution goes into either
PreparedStatement.executeQuery/executeUpdate.  Both of these are
blocking until some rows are available or the update has completed,
respectively.  For a query involving group by/order by executeQuery
can't return until all the aggregation and ordering logic has been done
in the database.

During this potentially-prolonged period the user would like to cancel
the execution, which would involve calling PreparedStatement.cancel.  I
have tested this between two threads, one executing the other calling
cancel, with a limited number of JDBC drivers (jtds for SQLServer and
Oracle's own driver) and it seems to work ok.

The issue is slightly compounded by the query could be one of several
that executes in sequence which I may want to cancel at any time.  I
could just add cancel hooks at each "depth" of my libraries down to the
JDBC level so if a user cancels the overall process it would work it's
way down through the executing process until the current JDBC call gets
cancelled.  I just wondered if there was a more elegant/preferred way of
doing it using interrupt().  I am mildly concerned that it could
interrupt nio channel traffic and close the channel as a consequence but
assuming I can avoid that is this a sensible approach?

Cheers.

Mike.

-----Original Message-----
From: Dhanji R. Prasanna [mailto:[hidden email]]
Sent: 07 June 2006 01:17
To: Mike Quilleash
Cc: [hidden email]
Subject: Re: [concurrency-interest] Cancellation convention

On 6/7/06, Mike Quilleash <[hidden email]> wrote:
>

>
> Specifically I'm interested in being able to cancel long running JDBC
> calls (using PreparedStatement.cancel()).

JDBC provides a setQueryTimeout which will throw a SQLException if the
PS runs past the timeout limit?
This should be the preferred way of timing out long-running queries, as
the semantics of concurrency with JDBC are obscure at best--unless Im
very much mistaken and have misunderstood your problem.

______________________________________________________________________
This email has been scanned by the MessageLabs Email Security System.
For more information please visit http://www.messagelabs.com/email 
______________________________________________________________________

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Joe Bowbeer
Mike Quilleash <[hidden email]> wrote:
>
> Specifically I'm interested in being able to cancel long running
> JDBC calls (using PreparedStatement.cancel()).  Would a sensible
> way be to fork off the PreparedStatement execution in another thread
> and have the main thread wait for the second thread to complete, but
> if it is interrupted then call the PreparedStatement.cancel() and throw
> an exception out to indicate the cancellation succeeded?  Just after
> some best practice advice.

Assuming you've scheduled the request as a FutureTask, I would call
task.cancel(false) to cancel it.  This will void the task -- without
sending an interrupt into JDBC land.

Then, assuming it's reliable(*), I would override the protected done()
method in your FutureTask so that it will cancel the prepared
statement if the task was cancelled.

  protected void done() {
    if (isCancelled()) {
      try {
        preparedStatement.cancel();
      } catch (SQLException ex) {
        // oops!
      }
    }
  }

(*) The spec says Statement.cancel() will:

"Cancels this Statement object if both the DBMS and driver support
aborting an SQL statement. This method can be used by one thread to
cancel a statement that is being executed by another thread."

--
Joe Bowbeer ~ joebowbeer.thruhere.net
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Kazimierz Pogoda
In reply to this post by Mike Quilleash
On Tue, 2006-06-06 at 19:24 +0100, Mike Quilleash wrote:

> Specifically I'm interested in being able to cancel long running JDBC
> calls (using PreparedStatement.cancel()).  Would a sensible way be to
> fork off the PreparedStatement execution in another thread and have
> the main thread wait for the second thread to complete, but if it is
> interrupted then call the PreparedStatement.cancel() and throw an
> exception out to indicate the cancellation succeeded?  Just after some
> best practice advice.

Cancellation of PreparedStatement (CallableStatement in fact) that was
executed by another thread works for me, but there is one issue. It
looks like oracle jdbc driver implementations react to cancel() method
on PreparedStatement only in case when it is hanging on the network
socket. So if we call cancel() on statement before executing thread is
"deep enough" in the execution, cancellation simply doesn't work. To
workaround this i wrote the code that analyzes stack trace of the
statement executor thread:

    private boolean isAwaitingEvent() {
        final StackTraceElement[] stackTrace = getStatementExecutorThread().getStackTrace();
        if (stackTrace.length < 2) {   //it is probably impossible, but maybe is thread is just started, so let's check
            return false;
        }
        final String recentClass = stackTrace[0].getClassName();
        final String recentMethod = stackTrace[0].getMethodName();

        if (!"java.net.SocketInputStream".equals(recentClass) && "socketRead0".equals(recentMethod)) {   //make sure we are hanging on socket
            return false;
        }

        boolean foundCallable = false;
        final String thisClass = getClass().getName();
        for (int i = 1; i < stackTrace.length; i++) {
            final String className = stackTrace[i].getClassName();
            final String methodName = stackTrace[i].getMethodName();
            if ((!foundCallable) && "oracle.jdbc.driver.OracleCallableStatement".equals(className) && "execute".equals(methodName)) {
                foundCallable = true;
                continue;
            } else if (thisClass.equals(className) && "awaitEvent".equals(methodName)) {
                return true;
            }
        }
        return false;
    }


And I'm waiting for this method to return true before I call cancel() on
the statement. Has anyone idea for better workaround?

--
"the spirit can only withdraw into a corner. And yet, it is not at all
 worn out or superfluous, but, like an (eternal) witness, floats above
 the rubble of culture - almost like an  avenger of God.  As though it
 awaited a new incarnation."
                                        -- Ludwig Wittgenstein --
Kazimierz Pogoda
mailto:[hidden email]

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Dhanji R. Prasanna
Hi, interesting solution. I cant really think of a better one but equally messy:

When the user cancels the request, go into a thread that keeps calling
cancel on the connection until a flag is set by the canceled thread:

//elsewhere in the class
Semaphore s = new Semaphore(1);

//this is the statement being executed
{
s.acquire();
ps.execute();
s.release();
}

//canceling thread
while (s.availablePermits() < 1) {
ps.cancel();
}

Im quite sure a more elegant synchronization solution can be found
with Futures or Locks. In any case I wouldn't be encouraged to use
this method without knowing exactly what the underlying JDBC driver
does on a Statement.cancel().

An unrelated comment: for efficiency and type safety in your
stacktrace analyzer solution you might want to compare:

java.net.SocketStream.class.equals(stackTrace[i])
//etc.

instead of comparing strings.


On 6/8/06, Kazimierz Pogoda <[hidden email]> wrote:

> On Tue, 2006-06-06 at 19:24 +0100, Mike Quilleash wrote:
>
> > Specifically I'm interested in being able to cancel long running JDBC
> > calls (using PreparedStatement.cancel()).  Would a sensible way be to
> > fork off the PreparedStatement execution in another thread and have
> > the main thread wait for the second thread to complete, but if it is
> > interrupted then call the PreparedStatement.cancel() and throw an
> > exception out to indicate the cancellation succeeded?  Just after some
> > best practice advice.
>
> Cancellation of PreparedStatement (CallableStatement in fact) that was
> executed by another thread works for me, but there is one issue. It
> looks like oracle jdbc driver implementations react to cancel() method
> on PreparedStatement only in case when it is hanging on the network
> socket. So if we call cancel() on statement before executing thread is
> "deep enough" in the execution, cancellation simply doesn't work. To
> workaround this i wrote the code that analyzes stack trace of the
> statement executor thread:
>
>     private boolean isAwaitingEvent() {
>         final StackTraceElement[] stackTrace = getStatementExecutorThread().getStackTrace();
>         if (stackTrace.length < 2) {   //it is probably impossible, but maybe is thread is just started, so let's check
>             return false;
>         }
>         final String recentClass = stackTrace[0].getClassName();
>         final String recentMethod = stackTrace[0].getMethodName();
>
>         if (!"java.net.SocketInputStream".equals(recentClass) && "socketRead0".equals(recentMethod)) {   //make sure we are hanging on socket
>             return false;
>         }
>
>         boolean foundCallable = false;
>         final String thisClass = getClass().getName();
>         for (int i = 1; i < stackTrace.length; i++) {
>             final String className = stackTrace[i].getClassName();
>             final String methodName = stackTrace[i].getMethodName();
>             if ((!foundCallable) && "oracle.jdbc.driver.OracleCallableStatement".equals(className) && "execute".equals(methodName)) {
>                 foundCallable = true;
>                 continue;
>             } else if (thisClass.equals(className) && "awaitEvent".equals(methodName)) {
>                 return true;
>             }
>         }
>         return false;
>     }
>
>
> And I'm waiting for this method to return true before I call cancel() on
> the statement. Has anyone idea for better workaround?
>
> --
> "the spirit can only withdraw into a corner. And yet, it is not at all
>  worn out or superfluous, but, like an (eternal) witness, floats above
>  the rubble of culture - almost like an  avenger of God.  As though it
>  awaited a new incarnation."
>                                         -- Ludwig Wittgenstein --
> Kazimierz Pogoda
> mailto:[hidden email]
>
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
>
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Kazimierz Pogoda
On Thu, 2006-06-08 at 10:52 +1000, Dhanji R. Prasanna wrote:

> ... Im quite sure a more elegant synchronization solution can be found
> with Futures or Locks. In any case I wouldn't be encouraged to use
> this method without knowing exactly what the underlying JDBC driver
> does on a Statement.cancel().

Thanks for your hints. I must check if it is possible to make several
cancellations on the same statement. My code is strongly bind to oracle,
thus I have only one jdbc driver to check. :)

> An unrelated comment: for efficiency and type safety in your
> stacktrace analyzer solution you might want to compare:
>
> java.net.SocketStream.class.equals(stackTrace[i])

I don't grasp the idea? StackTraceElement is not an instance of Class?
Do you suggest me to make:

Class.forName(stackTrace[i].getClassName())

every time my metod is called?

--
"the spirit can only withdraw into a corner. And yet, it is not at all
 worn out or superfluous, but, like an (eternal) witness, floats above
 the rubble of culture - almost like an  avenger of God.  As though it
 awaited a new incarnation."
                                        -- Ludwig Wittgenstein --
Kazimierz Pogoda
mailto:[hidden email]

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Dhanji R. Prasanna
> > java.net.SocketStream.class.equals(stackTrace[i])
>
> I don't grasp the idea? StackTraceElement is not an instance of Class?
> Do you suggest me to make:
>
> Class.forName(stackTrace[i].getClassName())
>
> every time my metod is called?
>

oops my bad, it should have read:

java.net.SocketStream.class.getName().equals(stackTrace[i].getClassName())

this way there is *some* type safety instead of directly comparing
strings/literals.
Dhanji
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Kazimierz Pogoda
On Fri, 2006-06-09 at 09:38 +1000, Dhanji R. Prasanna wrote:

> > > java.net.SocketStream.class.equals(stackTrace[i])
> >
> > I don't grasp the idea? StackTraceElement is not an instance of Class?
> > Do you suggest me to make:
> >
> > Class.forName(stackTrace[i].getClassName())
> >
> > every time my metod is called?
> >
>
> oops my bad, it should have read:
>
> java.net.SocketStream.class.getName().equals(stackTrace[i].getClassName())

Unfortunately java.net.SocketInputStream is a friendly class (default
class modifier) :(

--
"the spirit can only withdraw into a corner. And yet, it is not at all
 worn out or superfluous, but, like an (eternal) witness, floats above
 the rubble of culture - almost like an  avenger of God.  As though it
 awaited a new incarnation."
                                        -- Ludwig Wittgenstein --
Kazimierz Pogoda
mailto:[hidden email]

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Cancellation convention

Dhanji R. Prasanna
> Unfortunately java.net.SocketInputStream is a friendly class (default
> class modifier) :(
>

doh =(
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest