Skip to content

Commit 736f959

Browse files
committed
fix: replace syncronization in Connection.close with compareAndSet
Statement can be synchronized during query execution, and we don't want .close() to wait for the statement completion.
1 parent 4673fd2 commit 736f959

2 files changed

Lines changed: 23 additions & 10 deletions

File tree

pgjdbc/src/main/java/org/postgresql/jdbc/PgStatement.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,10 @@ public void setCursorName(String name) throws SQLException {
520520
// No-op.
521521
}
522522

523-
private volatile boolean isClosed = false;
523+
private volatile int isClosed = 0;
524+
private static final AtomicIntegerFieldUpdater<PgStatement> IS_CLOSED_UPDATER =
525+
AtomicIntegerFieldUpdater.newUpdater(
526+
PgStatement.class, "isClosed");
524527

525528
@Override
526529
public int getUpdateCount() throws SQLException {
@@ -673,11 +676,8 @@ public void clearWarnings() throws SQLException {
673676
*/
674677
public final void close() throws SQLException {
675678
// closing an already closed Statement is a no-op.
676-
synchronized (this) {
677-
if (isClosed) {
678-
return;
679-
}
680-
isClosed = true;
679+
if (!IS_CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
680+
return;
681681
}
682682

683683
cancel();
@@ -1138,7 +1138,7 @@ public long executeLargeUpdate(String sql, String @Nullable [] columnNames) thro
11381138
}
11391139

11401140
public boolean isClosed() throws SQLException {
1141-
return isClosed;
1141+
return isClosed == 1;
11421142
}
11431143

11441144
public void setPoolable(boolean poolable) throws SQLException {

pgjdbc/src/test/java/org/postgresql/test/jdbc2/StatementTest.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import java.sql.SQLException;
3333
import java.sql.SQLWarning;
3434
import java.sql.Statement;
35+
import java.util.ArrayList;
3536
import java.util.HashMap;
37+
import java.util.List;
3638
import java.util.Map;
3739
import java.util.Properties;
3840
import java.util.Random;
@@ -861,7 +863,7 @@ public void testMultipleCancels() throws Exception {
861863
public void testCancelQueryWithBrokenNetwork() throws SQLException, IOException, InterruptedException {
862864
// check that stmt.cancel() doesn't hang forever if the network is broken
863865

864-
ExecutorService executor = Executors.newSingleThreadExecutor();
866+
ExecutorService executor = Executors.newCachedThreadPool();
865867

866868
try (StrangeProxyServer proxyServer = new StrangeProxyServer(TestUtil.getServer(), TestUtil.getPort())) {
867869
Properties props = new Properties();
@@ -875,6 +877,9 @@ public void testCancelQueryWithBrokenNetwork() throws SQLException, IOException,
875877
proxyServer.stopForwardingAllClients();
876878

877879
stmt.cancel();
880+
// Note: network is still inaccessible, so the statement execution is still in progress.
881+
// So we abort the connection to allow implicit conn.close()
882+
conn.abort(executor);
878883
}
879884
}
880885

@@ -940,12 +945,13 @@ public Void call() throws Exception {
940945
}
941946

942947
@Test(timeout = 10000)
943-
public void testConcurrentIsValid() throws InterruptedException {
948+
public void testConcurrentIsValid() throws Throwable {
944949
ExecutorService executor = Executors.newCachedThreadPool();
945950
try {
951+
List<Future<?>> results = new ArrayList<>();
946952
Random rnd = new Random();
947953
for (int i = 0; i < 10; i++) {
948-
executor.submit(() -> {
954+
Future<?> future = executor.submit(() -> {
949955
try {
950956
for (int j = 0; j < 50; j++) {
951957
con.isValid(1);
@@ -969,7 +975,14 @@ public void testConcurrentIsValid() throws InterruptedException {
969975
throw new RuntimeException(e);
970976
}
971977
});
978+
results.add(future);
979+
}
980+
for (Future<?> result : results) {
981+
// Propagate exception if any
982+
result.get();
972983
}
984+
} catch (ExecutionException e) {
985+
throw e.getCause();
973986
} finally {
974987
executor.shutdown();
975988
executor.awaitTermination(10, TimeUnit.SECONDS);

0 commit comments

Comments
 (0)