修改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.PGConnection;
import org.postgresql.jdbc.ClientLogic; import org.postgresql.jdbc.ClientLogic;
import org.postgresql.jdbc.FieldMetadata; import org.postgresql.jdbc.FieldMetadata;
import org.postgresql.jdbc.PgStatement;
import org.postgresql.jdbc.TimestampUtils; import org.postgresql.jdbc.TimestampUtils;
import org.postgresql.log.Log; import org.postgresql.log.Log;
import org.postgresql.util.LruCache; import org.postgresql.util.LruCache;
@ -17,6 +18,7 @@ import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/** /**
* Driver-internal connection interface. Application code should not use this interface. * 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; PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException;
public String getSocketAddress(); 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.util.concurrent.Executor;
import java.io.File; import java.io.File;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.postgresql.core.types.PGClob; import org.postgresql.core.types.PGClob;
import org.postgresql.core.types.PGBlob; import org.postgresql.core.types.PGBlob;
@ -103,6 +105,16 @@ public class PgConnection implements BaseConnection {
CONNECTION_INFO_REPORT_BLACK_LIST.put("PGDBNAME",""); 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: // Data initialized on construction:
// //
@ -2061,5 +2073,9 @@ public class PgConnection implements BaseConnection {
return this.socketAddress; 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 * 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 * 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 * 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; public volatile TimerTask cancelTimerTask = null;
private static final AtomicReferenceFieldUpdater<PgStatement, TimerTask> CANCEL_TIMER_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(PgStatement.class, TimerTask.class, "cancelTimerTask");
/** /**
* Protects statement from out-of-order cancels. It protects from both * 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() { TimerTask cancelTask = new TimerTask() {
public void run() { public void run() {
try { 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 // Nothing to do here, statement has already finished and cleared
// cancelTimerTask reference // cancelTimerTask reference
return; 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); connection.addTimerTask(cancelTask, timeout);
} }
@ -1202,12 +1200,12 @@ public class PgStatement implements Statement, BaseStatement {
* never invoke {@link #cancel()}. * never invoke {@link #cancel()}.
*/ */
private boolean cleanupTimer() { private boolean cleanupTimer() {
TimerTask timerTask = CANCEL_TIMER_UPDATER.get(this); TimerTask timerTask = connection.getTimerUpdater().get(this);
if (timerTask == null) { if (timerTask == null) {
// If timeout is zero, then timer task did not exist, so we safely report "all clear" // If timeout is zero, then timer task did not exist, so we safely report "all clear"
return timeout == 0; 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 // Failed to update reference -> timer has just fired, so we must wait for the query state to
// become "cancelling". // become "cancelling".
return false; return false;