From cbeeabb4af87f514920d11746c7c28577d5b3c52 Mon Sep 17 00:00:00 2001 From: Ken Wenzel Date: Tue, 17 Oct 2023 17:15:25 +0200 Subject: [PATCH] GH-4817 LMDB: Make close method of LmdbRecordIterator thread-safe. Ensures that a lock is used if close() is called asynchronously from another thread than next(). --- .../rdf4j/sail/lmdb/LmdbRecordIterator.java | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java index bcdd074cb07..530a77a6ebe 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java @@ -54,7 +54,7 @@ class LmdbRecordIterator implements RecordIterator { private final int dbi; - private boolean closed = false; + private volatile boolean closed = false; private final MDBVal keyData; @@ -72,6 +72,8 @@ class LmdbRecordIterator implements RecordIterator { private final StampedLock txnLock; + private final Thread ownerThread = Thread.currentThread(); + LmdbRecordIterator(Pool pool, TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, long context, boolean explicit, Txn txnRef) throws IOException { this.pool = pool; @@ -140,7 +142,7 @@ public long[] next() throws IOException { lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); } if (lastResult != 0) { - close(); + closeInternal(false); return null; } } @@ -177,30 +179,45 @@ public long[] next() throws IOException { return quad; } } - close(); + closeInternal(false); return null; } finally { txnLock.unlockRead(stamp); } } - @Override - public void close() throws IOException { + private void closeInternal(boolean maybeCalledAsync) { if (!closed) { + long stamp; + if (maybeCalledAsync && ownerThread != Thread.currentThread()) { + stamp = txnLock.writeLock(); + } else { + stamp = 0; + } try { - mdb_cursor_close(cursor); - pool.free(keyData); - pool.free(valueData); - if (minKeyBuf != null) { - pool.free(minKeyBuf); - } - if (maxKey != null) { - pool.free(maxKeyBuf); - pool.free(maxKey); + if (!closed) { + mdb_cursor_close(cursor); + pool.free(keyData); + pool.free(valueData); + if (minKeyBuf != null) { + pool.free(minKeyBuf); + } + if (maxKey != null) { + pool.free(maxKeyBuf); + pool.free(maxKey); + } } } finally { closed = true; + if (stamp != 0) { + txnLock.unlockWrite(stamp); + } } } } + + @Override + public void close() throws IOException { + closeInternal(true); + } }