LUCENE-4025: add RefereneManager.maybeRefreshBlocking()

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1332699 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Shai Erera 2012-05-01 15:25:44 +00:00
parent 5ede77e922
commit 77ababf249
3 changed files with 74 additions and 28 deletions

View File

@ -846,6 +846,9 @@ New features
* LUCENE-4004: Add DisjunctionMaxQuery support to the xml query parser. * LUCENE-4004: Add DisjunctionMaxQuery support to the xml query parser.
(Benson Margulies via Robert Muir) (Benson Margulies via Robert Muir)
* LUCENE-4025: Add maybeRefreshBlocking to ReferenceManager, to let a caller
block until the refresh logic has been executed. (Shai Erera, Mike McCandless)
Optimizations Optimizations
* LUCENE-2588: Don't store unnecessary suffixes when writing the terms * LUCENE-2588: Don't store unnecessary suffixes when writing the terms

View File

@ -19,7 +19,8 @@ package org.apache.lucene.search;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; 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; import org.apache.lucene.store.AlreadyClosedException;
@ -42,7 +43,7 @@ public abstract class ReferenceManager<G> implements Closeable {
protected volatile G current; protected volatile G current;
private final Semaphore reopenLock = new Semaphore(1); private final Lock refreshLock = new ReentrantLock();
private void ensureOpen() { private void ensureOpen() {
if (current == null) { if (current == null) {
@ -108,9 +109,42 @@ public abstract class ReferenceManager<G> implements Closeable {
protected void afterClose() throws IOException { 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 * You must call this (or {@link #maybeRefreshBlocking()}), periodically, if
* return refreshed instances. * you want that {@link #acquire()} will return refreshed instances.
* *
* <p> * <p>
* <b>Threads</b>: it's fine for more than one thread to call this at once. * <b>Threads</b>: it's fine for more than one thread to call this at once.
@ -121,43 +155,48 @@ public abstract class ReferenceManager<G> implements Closeable {
* refresh to complete. * refresh to complete.
* *
* <p> * <p>
* If this method returns true it means the calling thread either refreshed * If this method returns true it means the calling thread either refreshed or
* or that there were no changes to refresh. If it returns false it means another * that there were no changes to refresh. If it returns false it means another
* thread is currently refreshing. * thread is currently refreshing.
*/ */
public final boolean maybeRefresh() throws IOException { public final boolean maybeRefresh() throws IOException {
ensureOpen(); ensureOpen();
// Ensure only 1 thread does reopen at once; other threads just return immediately: // 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) { if (doTryRefresh) {
try { try {
final G reference = acquire(); doMaybeRefresh();
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 { } finally {
reopenLock.release(); refreshLock.unlock();
} }
} }
return doTryRefresh; return doTryRefresh;
} }
/**
* You must call this (or {@link #maybeRefresh()}), periodically, if you want
* that {@link #acquire()} will return refreshed instances.
*
* <p>
* <b>Threads</b>: unlike {@link #maybeRefresh()}, if another thread is
* currently refreshing, this method blocks until that thread completes. 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 /** Called after swapReference has installed a new
* instance. */ * instance. */

View File

@ -108,7 +108,11 @@ public class TestSearcherManager extends ThreadedIndexingAndSearchingTestCase {
Thread.sleep(_TestUtil.nextInt(random(), 1, 100)); Thread.sleep(_TestUtil.nextInt(random(), 1, 100));
writer.commit(); writer.commit();
Thread.sleep(_TestUtil.nextInt(random(), 1, 5)); Thread.sleep(_TestUtil.nextInt(random(), 1, 5));
if (mgr.maybeRefresh()) { boolean block = random().nextBoolean();
if (block) {
mgr.maybeRefreshBlocking();
lifetimeMGR.prune(pruner);
} else if (mgr.maybeRefresh()) {
lifetimeMGR.prune(pruner); lifetimeMGR.prune(pruner);
} }
} }