Avoiding Statement.cancel() races - Mailing list pgsql-jdbc
From | Oliver Jowett |
---|---|
Subject | Avoiding Statement.cancel() races |
Date | |
Msg-id | 20031121021413.GA19307@opencloud.com Whole thread Raw |
Responses |
Re: Avoiding Statement.cancel() races
|
List | pgsql-jdbc |
I'm trying to work out how to avoid race conditions when using cancel() to do graceful shutdown of a DB access thread, but it seems like there's a race condition in the JDBC API itself. I have a worker thread doing DB work. Another thread decides that it wants to stop this worker thread (in our case, due to cluster partition). So it does the usual set-flag-and-notify trick to inform the DB thread. However that worker thread might be currently running a query -- and in turn that query may block for an extended period of time (for example, if it's waiting on a backend lock that's held by a client connection from the other side of the partition, then it'll only continue when that other client connection finally dies which could take up to the TCP abort interval). I'd like to write code that looks something like this: private volatile boolean stopping = false; private Statement currentStatement = null; public void run() { // Worker thread while (!stopping) { // ... wait() for more work ... synchronized (this) { if (stopping) throw new SQLException("db thread cancelled"); currentStatement = /* ... */; } results = currentStatement.executeUpdate(); synchronized (this) { currentStatement = null; } // ... } } public void stopWorkerThread() { synchronized (this) { stopping = true; if (currentStatement != null) currentStatement.cancel(); this.notify(); } } However there's an obvious race condition here between starting query execution and cancelling the statement -- it's possible for stopWorkerThread() to run at just the wrong time and "cancel" the statement before it starts execution, having no effect and leaving the worker thread still running a query (at least as I understand cancel() .. that's how it's implemented in the postgresql driver anyway). Extending the synchronized block to enclose 'someStatement.execute()' doesn't help since then stopWorkerThread() can't enter that monitor while the query is executing, which defeats the purpose of doing this in the first place. This seems like a design flaw in the JDBC API. Is there a way to solve this problem that I'm missing? Perhaps cancel() should be "permanent" i.e. once cancelled, a Statement cannot be reused: all subsequent attempts at query execution are immediately cancelled. This seems simpler, but I'd have expected any behaviour like this to be explicitly spelled out in the cancel() javadoc if it was intended to work that way. Alternatively, we could add a cancelPermanently() method to PGstatement that has this behaviour as a postgresql extension. Any thoughts? -O
pgsql-jdbc by date: