Skip to content

Commit fc3ec7a

Browse files
yuyu.zhaoclaude
andcommitted
Gate cleanUp() with read lock; simplify close() teardown
Make the periodic cleanUp() acquire the lifecycle read lock so its refKeeper removals and scanner closes cannot overlap close()'s write-locked teardown. With every refKeeper mutator now gated by the lock, close() can iterate the set directly under the write lock and clear it, dropping the synchronized snapshot and the redundant trailing cleanUp() call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 855c057 commit fc3ec7a

1 file changed

Lines changed: 12 additions & 10 deletions

File tree

src/main/java/com/gliwka/hyperscan/util/ScopedPatternFilterFactory.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ public static ScopedPatternFilterFactory<Pattern> ofPatterns(Iterable<Pattern> p
203203

204204

205205
// This is an instance method that knows about this instance's queue and refKeeper.
206+
// Holds the read lock so its scanner closes and refKeeper removals cannot overlap close()'s
207+
// teardown (which holds the write lock and frees the shared database).
206208
private void cleanUp() {
209+
lifecycleLock.readLock().lock();
207210
try {
208211
PatternFilterCleaner ref;
209212
while ((ref = (PatternFilterCleaner) referenceQueue.poll()) != null) {
@@ -212,6 +215,8 @@ private void cleanUp() {
212215
}
213216
} catch (Exception e) {
214217
// Log or handle exception
218+
} finally {
219+
lifecycleLock.readLock().unlock();
215220
}
216221
}
217222

@@ -285,22 +290,19 @@ public void close() {
285290
// after this sees closed == true and bails out.
286291
lifecycleLock.writeLock().lock();
287292
try {
288-
// Close every live filter's scanner before freeing the shared database. Each close
289-
// action synchronizes on its scanner, so it waits out any in-flight scan (and the
290-
// closed flag set above blocks new ones) — guaranteeing no scan can touch the
291-
// database once we free it.
292-
PatternFilterCleaner[] cleaners;
293-
synchronized (refKeeper) {
294-
cleaners = refKeeper.toArray(new PatternFilterCleaner[0]);
295-
}
296-
for (PatternFilterCleaner cleaner : cleaners) {
293+
// The write lock excludes createFilter() and cleanUp() (both read-lock holders), so
294+
// refKeeper is structurally stable here and can be iterated directly. Close every
295+
// live filter's scanner before freeing the shared database: each close action
296+
// synchronizes on its scanner, so it waits out any in-flight scan (and the closed
297+
// flag set above blocks new ones) — guaranteeing no scan can touch the database once
298+
// we free it.
299+
for (PatternFilterCleaner cleaner : refKeeper) {
297300
cleaner.clean();
298301
}
299302
// All scanners (and their scratch) are now closed; release the shared native database.
300303
if (database != null) {
301304
database.close();
302305
}
303-
cleanUp();
304306
refKeeper.clear();
305307
} finally {
306308
lifecycleLock.writeLock().unlock();

0 commit comments

Comments
 (0)