|
42 | 42 | import java.sql.SQLException; |
43 | 43 | import java.util.Map; |
44 | 44 | import java.util.WeakHashMap; |
| 45 | +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; |
45 | 46 | import java.util.function.Function; |
46 | 47 |
|
47 | 48 | import static java.util.Objects.requireNonNull; |
@@ -253,54 +254,77 @@ private void deferredExceptionHandler(Exception exception) { |
253 | 254 | } |
254 | 255 | } |
255 | 256 |
|
| 257 | + @SuppressWarnings("resource") |
256 | 258 | private static final class CleanupAction implements Runnable, StatementListener, DatabaseListener { |
257 | 259 |
|
| 260 | + private static final AtomicReferenceFieldUpdater<CleanupAction, FbWireDatabase> databaseUpdater = |
| 261 | + AtomicReferenceFieldUpdater.newUpdater(CleanupAction.class, FbWireDatabase.class, "database"); |
| 262 | + |
| 263 | + private static final DeferredAction CLEANUP_FREE_DEFERRED_ACTION = new DeferredAction() { |
| 264 | + @Override |
| 265 | + public void processResponse(Response response) { |
| 266 | + // nothing to do |
| 267 | + } |
| 268 | + |
| 269 | + @Override |
| 270 | + public boolean requiresSync() { |
| 271 | + return true; |
| 272 | + } |
| 273 | + }; |
| 274 | + |
258 | 275 | private final int handle; |
259 | | - @SuppressWarnings("java:S3077") |
260 | 276 | private volatile FbWireDatabase database; |
261 | 277 |
|
262 | 278 | private CleanupAction(AbstractFbWireStatement statement) { |
263 | 279 | // NOTE: Care should be taken not to retain a handle to statement itself here |
264 | 280 | handle = statement.getHandle(); |
265 | | - database = statement.getDatabase(); |
| 281 | + FbWireDatabase database = statement.getDatabase(); |
| 282 | + databaseUpdater.set(this, database); |
266 | 283 | database.addWeakDatabaseListener(this); |
267 | 284 | statement.addWeakStatementListener(this); |
268 | 285 | } |
269 | 286 |
|
270 | 287 | @Override |
271 | 288 | public void statementStateChanged(FbStatement sender, StatementState newState, StatementState previousState) { |
272 | 289 | if (newState == StatementState.CLOSING) { |
273 | | - FbDatabase database = this.database; |
274 | | - if (database != null) { |
275 | | - release(database); |
276 | | - } |
| 290 | + releaseDatabaseReference(); |
277 | 291 | sender.removeStatementListener(this); |
278 | 292 | } |
279 | 293 | } |
280 | 294 |
|
281 | 295 | @Override |
282 | 296 | public void detaching(FbDatabase database) { |
283 | | - release(database); |
| 297 | + releaseDatabaseReference(); |
284 | 298 | } |
285 | 299 |
|
286 | | - private void release(FbDatabase database) { |
287 | | - this.database = null; |
288 | | - database.removeDatabaseListener(this); |
| 300 | + private void releaseDatabaseReference() { |
| 301 | + FbWireDatabase database = databaseUpdater.getAndSet(this, null); |
| 302 | + if (database != null) { |
| 303 | + database.removeDatabaseListener(this); |
| 304 | + } |
| 305 | + } |
| 306 | + |
| 307 | + private FbWireDatabase releaseAndGetDatabaseReference() { |
| 308 | + FbWireDatabase database = databaseUpdater.getAndSet(this, null); |
| 309 | + if (database != null) { |
| 310 | + database.removeDatabaseListener(this); |
| 311 | + } |
| 312 | + return database; |
289 | 313 | } |
290 | 314 |
|
291 | 315 | @Override |
292 | 316 | public void run() { |
293 | | - FbWireDatabase database = this.database; |
| 317 | + FbWireDatabase database = releaseAndGetDatabaseReference(); |
294 | 318 | if (database == null) return; |
295 | | - release(database); |
296 | 319 | try (LockCloseable ignored = database.withLock()) { |
297 | 320 | if (!database.isAttached()) return; |
298 | 321 | XdrOutputStream xdrOut = database.getXdrStreamAccess().getXdrOut(); |
299 | 322 | xdrOut.writeInt(WireProtocolConstants.op_free_statement); // p_operation |
300 | 323 | xdrOut.writeInt(handle); // p_sqlfree_statement |
301 | 324 | xdrOut.writeInt(ISCConstants.DSQL_drop); // p_sqlfree_option |
302 | 325 | xdrOut.flush(); |
303 | | - database.enqueueDeferredAction(DeferredAction.NO_OP_INSTANCE); |
| 326 | + // TODO: This may process deferred actions on the cleaner thread, we may want to change that |
| 327 | + database.enqueueDeferredAction(CLEANUP_FREE_DEFERRED_ACTION); |
304 | 328 | } catch (SQLException | IOException e) { |
305 | 329 | System.getLogger(getClass().getName()).log(System.Logger.Level.TRACE, |
306 | 330 | "Ignored exception during statement clean up", e); |
|
0 commit comments