diff --git a/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java index 26f7ee02ed..939f0ac931 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java @@ -61,6 +61,77 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc private int batchSize = 1000; + /** + * Although not really allowed, locks are being held by daemon threads. This + * field holds the thread with the lock, so that other threads that 'wait' + * don't wait forever if a daemon thread that has the lock is killed on shutdown. + * + * On entry to a synchronized method or block the value is either null or the + * current Thread (if a nested call). On exit it is set back to its original value + * unless the Thread dies. + * + * Prior to calling wait it must be set to null and on return must be set back to + * the current thread. + * + * If a daemon thread has just been killed, on return from the wait, + * it will not be null and the thread holding the lock will not be alive. + * + * The alternative to this approach might include: + * - Making all threads that use this class daemons (might be simplest) + */ + private static Thread threadHoldingLock; + + // Helper class to keep track of which Thread has the lock, so we can give up if it dies. + private abstract class SynchronizedHelper + { + public Result executeNoInterruptedException() + { + try { + return execute(); + } catch (InterruptedException e) { + // ignore + return null; + } + } + + @SuppressWarnings("finally") + public Result execute() throws InterruptedException + { + Thread origThreadHoldingLock = threadHoldingLock; + threadHoldingLock = Thread.currentThread(); + try + { + return run(); + } + catch (ThreadDeath threadDeath) + { + origThreadHoldingLock = null; + throw threadDeath; + } + finally + { + threadHoldingLock = origThreadHoldingLock; + } + } + + public abstract Result run() throws InterruptedException; + + // Does a wait(10000). Returns true if the Thread that had the lock has died. + public boolean waitAndCheckForSubsystemShutdown() throws InterruptedException + { + try + { + threadHoldingLock = null; + FullTextSearchIndexerImpl.this.wait(10000); + return threadHoldingLock != null && !threadHoldingLock.isAlive(); + } + finally + { + threadHoldingLock = Thread.currentThread(); + } + } + } + /** * */ @@ -75,13 +146,20 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc * * @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#requiresIndex(org.alfresco.repo.ref.StoreRef) */ - public synchronized void requiresIndex(StoreRef storeRef) + public synchronized void requiresIndex(final StoreRef storeRef) { - if(s_logger.isDebugEnabled()) - { - s_logger.debug("FTS index request for "+storeRef); - } - requiresIndex.add(storeRef); + new SynchronizedHelper() + { + public Void run() + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("FTS index request for "+storeRef); + } + requiresIndex.add(storeRef); + return null; + } + }.executeNoInterruptedException(); } /* @@ -89,28 +167,35 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc * * @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#indexCompleted(org.alfresco.repo.ref.StoreRef, int, java.lang.Exception) */ - public synchronized void indexCompleted(StoreRef storeRef, int remaining, Throwable t) + public synchronized void indexCompleted(final StoreRef storeRef, final int remaining, final Throwable t) { - try - { - if(s_logger.isDebugEnabled()) - { - s_logger.debug("FTS index completed for "+storeRef+" ... "+remaining+ " remaining"); - } - indexing.remove(storeRef); - if ((remaining > 0) || (t != null)) - { - requiresIndex(storeRef); - } - if (t != null) - { - throw new FTSIndexerException(t); - } - } - finally - { - this.notifyAll(); - } + new SynchronizedHelper() + { + public Void run() + { + try + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("FTS index completed for "+storeRef+" ... "+remaining+ " remaining"); + } + indexing.remove(storeRef); + if ((remaining > 0) || (t != null)) + { + requiresIndex(storeRef); + } + if (t != null) + { + throw new FTSIndexerException(t); + } + } + finally + { + FullTextSearchIndexerImpl.this.notifyAll(); + } + return null; + } + }.executeNoInterruptedException(); } /* @@ -120,29 +205,40 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc */ public synchronized void pause() throws InterruptedException { - pauseCount++; - if(s_logger.isTraceEnabled()) - { - s_logger.trace("..Waiting "+pauseCount+" id is "+this); - } - while ((indexing.size() > 0)) - { - if(s_logger.isTraceEnabled()) - { - s_logger.trace("Pause: Waiting with count of "+indexing.size()+" id is "+this); - } - this.wait(); - } - pauseCount--; - if (pauseCount == 0) - { - paused = true; - this.notifyAll(); // only resumers - } - if(s_logger.isTraceEnabled()) - { - s_logger.trace("..Remaining "+pauseCount +" paused = "+paused+" id is "+this); - } + new SynchronizedHelper() + { + public Void run() throws InterruptedException + { + pauseCount++; + if(s_logger.isTraceEnabled()) + { + s_logger.trace("..Waiting "+pauseCount+" id is "+this); + } + while ((indexing.size() > 0)) + { + if(s_logger.isTraceEnabled()) + { + s_logger.trace("Pause: Waiting with count of "+indexing.size()+" id is "+this); + } + + if (waitAndCheckForSubsystemShutdown()) + { + indexing.clear(); + } + } + pauseCount--; + if (pauseCount == 0) + { + paused = true; + FullTextSearchIndexerImpl.this.notifyAll(); // only resumers + } + if(s_logger.isTraceEnabled()) + { + s_logger.trace("..Remaining "+pauseCount +" paused = "+paused+" id is "+this); + } + return null; + } + }.execute(); } /* @@ -152,43 +248,63 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc */ public synchronized void resume() throws InterruptedException { - if (pauseCount == 0) - { - if(s_logger.isTraceEnabled()) - { - s_logger.trace("Direct resume"+" id is "+this); - } - paused = false; - } - else - { - while (pauseCount > 0) - { - if(s_logger.isTraceEnabled()) + new SynchronizedHelper() + { + public Void run() throws InterruptedException + { + if (pauseCount == 0) { - s_logger.trace("Resume waiting on "+pauseCount+" id is "+this); + if(s_logger.isTraceEnabled()) + { + s_logger.trace("Direct resume"+" id is "+this); + } + paused = false; } - this.wait(); - } - paused = false; - } + else + { + while (pauseCount > 0) + { + if(s_logger.isTraceEnabled()) + { + s_logger.trace("Resume waiting on "+pauseCount+" id is "+this); + } + + if (waitAndCheckForSubsystemShutdown()) + { + break; + } + } + paused = false; + } + return null; + } + }.execute(); } @SuppressWarnings("unused") private synchronized boolean isPaused() throws InterruptedException { - if (pauseCount == 0) - { - return paused; - } - else - { - while (pauseCount > 0) - { - this.wait(); - } - return paused; - } + return new SynchronizedHelper() + { + public Boolean run() throws InterruptedException + { + if (pauseCount == 0) + { + return paused; + } + else + { + while (pauseCount > 0) + { + if (waitAndCheckForSubsystemShutdown()) + { + break; + } + } + return paused; + } + } + }.execute(); } /* @@ -262,34 +378,40 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc private synchronized StoreRef getNextRef() { - if (paused || (pauseCount > 0)) - { - if(s_logger.isTraceEnabled()) - { - s_logger.trace("Indexing suspended - no store available - id is "+this); - } - return null; - } + return new SynchronizedHelper() + { + public StoreRef run() + { + if (paused || (pauseCount > 0)) + { + if(s_logger.isTraceEnabled()) + { + s_logger.trace("Indexing suspended - no store available - id is "+this); + } + return null; + } - StoreRef nextStoreRef = null; + StoreRef nextStoreRef = null; - for (StoreRef ref : requiresIndex) - { - if (!indexing.contains(ref)) - { - nextStoreRef = ref; - // FIFO - break; - } - } + for (StoreRef ref : requiresIndex) + { + if (!indexing.contains(ref)) + { + nextStoreRef = ref; + // FIFO + break; + } + } - if (nextStoreRef != null) - { - requiresIndex.remove(nextStoreRef); - indexing.add(nextStoreRef); - } + if (nextStoreRef != null) + { + requiresIndex.remove(nextStoreRef); + indexing.add(nextStoreRef); + } - return nextStoreRef; + return nextStoreRef; + } + }.executeNoInterruptedException(); } /** @@ -353,6 +475,4 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc requiresIndex.clear(); indexing.clear(); } - - }