Index: lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java =================================================================== --- lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java (revision 1332524) +++ lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java (working copy) @@ -20,6 +20,8 @@ import java.io.Closeable; import java.io.IOException; import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.apache.lucene.store.AlreadyClosedException; @@ -42,7 +44,7 @@ protected volatile G current; - private final Semaphore reopenLock = new Semaphore(1); + private final Lock refreshLock = new ReentrantLock(); private void ensureOpen() { if (current == null) { @@ -108,6 +110,39 @@ protected void afterClose() throws IOException { } + private void doMaybeRefresh() throws IOException { + // it's ok to call lock() here (blocking) because we're supposed to get here + // from either maybeRefreh() or maybeRefreshBlocking(), after the lock has + // already been obtained. Doing that protects us from an accidental bug + // where this method will be called outside the scope of refreshLock. + // Per ReentrantLock's javadoc, calling lock() by the same thread more than + // once is ok, as long as unlock() is called a matching number of times. + refreshLock.lock(); + try { + final G reference = acquire(); + try { + G newReference = refreshIfNeeded(reference); + if (newReference != null) { + assert newReference != reference : "refreshIfNeeded should return null if refresh wasn't needed"; + boolean success = false; + try { + swapReference(newReference); + success = true; + } finally { + if (!success) { + release(newReference); + } + } + } + } finally { + release(reference); + } + afterRefresh(); + } finally { + refreshLock.unlock(); + } + } + /** * You must call this, periodically, if you want that {@link #acquire()} will * return refreshed instances. @@ -129,36 +164,41 @@ ensureOpen(); // Ensure only 1 thread does reopen at once; other threads just return immediately: - final boolean doTryRefresh = reopenLock.tryAcquire(); + final boolean doTryRefresh = refreshLock.tryLock(); if (doTryRefresh) { try { - final G reference = acquire(); - try { - G newReference = refreshIfNeeded(reference); - if (newReference != null) { - assert newReference != reference : "refreshIfNeeded should return null if refresh wasn't needed"; - boolean success = false; - try { - swapReference(newReference); - success = true; - } finally { - if (!success) { - release(newReference); - } - } - } - } finally { - release(reference); - } - afterRefresh(); + doMaybeRefresh(); } finally { - reopenLock.release(); + refreshLock.unlock(); } } return doTryRefresh; } + + /** + * You must call this, periodically, if you want that {@link #acquire()} will + * return refreshed instances. + * + *

+ * Threads: unlike {@link #maybeRefresh()}, this method blocks until + * the refresh logic is executed (either actually refreshing the instance or + * not). It is useful if you want to guarantee that the next call to + * {@link #acquire()} will return a refreshed instance. Otherwise, consider + * using the non-blocking {@link #maybeRefresh()}. + */ + public final void maybeRefreshBlocking() throws IOException, InterruptedException { + ensureOpen(); + // Ensure only 1 thread does reopen at once + refreshLock.lock(); + try { + doMaybeRefresh(); + } finally { + refreshLock.lock(); + } + } + /** Called after swapReference has installed a new * instance. */ protected void afterRefresh() throws IOException {