修改cleanupTimer原子更新器
This commit is contained in:
@ -8,6 +8,7 @@ package org.postgresql.core;
|
||||
import org.postgresql.PGConnection;
|
||||
import org.postgresql.jdbc.ClientLogic;
|
||||
import org.postgresql.jdbc.FieldMetadata;
|
||||
import org.postgresql.jdbc.PgStatement;
|
||||
import org.postgresql.jdbc.TimestampUtils;
|
||||
import org.postgresql.log.Log;
|
||||
import org.postgresql.util.LruCache;
|
||||
@ -17,6 +18,7 @@ import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
|
||||
/**
|
||||
* Driver-internal connection interface. Application code should not use this interface.
|
||||
@ -228,4 +230,10 @@ public interface BaseConnection extends PGConnection, Connection {
|
||||
PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException;
|
||||
|
||||
public String getSocketAddress();
|
||||
|
||||
/**
|
||||
* Gets the timertask atomic updater for a statement
|
||||
* @return AtomicReferenceFieldUpdater<PgStatement, TimerTask>
|
||||
*/
|
||||
AtomicReferenceFieldUpdater<PgStatement, TimerTask> getTimerUpdater();
|
||||
}
|
||||
|
||||
@ -78,6 +78,8 @@ import java.util.TimerTask;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
|
||||
import org.postgresql.core.types.PGClob;
|
||||
import org.postgresql.core.types.PGBlob;
|
||||
|
||||
@ -103,6 +105,16 @@ public class PgConnection implements BaseConnection {
|
||||
CONNECTION_INFO_REPORT_BLACK_LIST.put("PGDBNAME","");
|
||||
}
|
||||
|
||||
/**set
|
||||
* Protects current statement from cancelTask starting, waiting for a bit, and waking up exactly
|
||||
* on subsequent query execution. The idea is to atomically compare and swap the reference to the
|
||||
* task, so the task can detect that statement executes different query than the one the
|
||||
* cancelTask was created. Note: the field must be set/get/compareAndSet via
|
||||
* {@link #CANCEL_TIMER_UPDATER} as per {@link AtomicReferenceFieldUpdater} javadoc.
|
||||
*/
|
||||
private AtomicReferenceFieldUpdater<PgStatement, TimerTask> CANCEL_TIMER_UPDATER =
|
||||
AtomicReferenceFieldUpdater.newUpdater(PgStatement.class, TimerTask.class, "cancelTimerTask");
|
||||
|
||||
//
|
||||
// Data initialized on construction:
|
||||
//
|
||||
@ -2061,5 +2073,9 @@ public class PgConnection implements BaseConnection {
|
||||
return this.socketAddress;
|
||||
}
|
||||
|
||||
public AtomicReferenceFieldUpdater<PgStatement, TimerTask> getTimerUpdater() {
|
||||
return CANCEL_TIMER_UPDATER;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -52,11 +52,9 @@ public class PgStatement implements Statement, BaseStatement {
|
||||
* on subsequent query execution. The idea is to atomically compare and swap the reference to the
|
||||
* task, so the task can detect that statement executes different query than the one the
|
||||
* cancelTask was created. Note: the field must be set/get/compareAndSet via
|
||||
* {@link #CANCEL_TIMER_UPDATER} as per {@link AtomicReferenceFieldUpdater} javadoc.
|
||||
* {@link PgConnection CANCEL_TIMER_UPDATER} as per {@link AtomicReferenceFieldUpdater} javadoc.
|
||||
*/
|
||||
private volatile TimerTask cancelTimerTask = null;
|
||||
private static final AtomicReferenceFieldUpdater<PgStatement, TimerTask> CANCEL_TIMER_UPDATER =
|
||||
AtomicReferenceFieldUpdater.newUpdater(PgStatement.class, TimerTask.class, "cancelTimerTask");
|
||||
public volatile TimerTask cancelTimerTask = null;
|
||||
|
||||
/**
|
||||
* Protects statement from out-of-order cancels. It protects from both
|
||||
@ -1181,7 +1179,7 @@ public class PgStatement implements Statement, BaseStatement {
|
||||
TimerTask cancelTask = new TimerTask() {
|
||||
public void run() {
|
||||
try {
|
||||
if (!CANCEL_TIMER_UPDATER.compareAndSet(PgStatement.this, this, null)) {
|
||||
if (!connection.getTimerUpdater().compareAndSet(PgStatement.this, this, null)) {
|
||||
// Nothing to do here, statement has already finished and cleared
|
||||
// cancelTimerTask reference
|
||||
return;
|
||||
@ -1193,7 +1191,7 @@ public class PgStatement implements Statement, BaseStatement {
|
||||
}
|
||||
};
|
||||
|
||||
CANCEL_TIMER_UPDATER.set(this, cancelTask);
|
||||
connection.getTimerUpdater().set(this, cancelTask);
|
||||
connection.addTimerTask(cancelTask, timeout);
|
||||
}
|
||||
|
||||
@ -1202,12 +1200,12 @@ public class PgStatement implements Statement, BaseStatement {
|
||||
* never invoke {@link #cancel()}.
|
||||
*/
|
||||
private boolean cleanupTimer() {
|
||||
TimerTask timerTask = CANCEL_TIMER_UPDATER.get(this);
|
||||
TimerTask timerTask = connection.getTimerUpdater().get(this);
|
||||
if (timerTask == null) {
|
||||
// If timeout is zero, then timer task did not exist, so we safely report "all clear"
|
||||
return timeout == 0;
|
||||
}
|
||||
if (!CANCEL_TIMER_UPDATER.compareAndSet(this, timerTask, null)) {
|
||||
if (!connection.getTimerUpdater().compareAndSet(this, timerTask, null)) {
|
||||
// Failed to update reference -> timer has just fired, so we must wait for the query state to
|
||||
// become "cancelling".
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user