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