修改cleanupTimer原子更新器

This commit is contained in:
chen-czywj
2023-02-24 15:39:51 +08:00
parent 803aed58bc
commit c74202ee5f
3 changed files with 30 additions and 8 deletions

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;