Merged HEAD-BUG-FIX (4.3/Cloud) to HEAD (4.3/Cloud)

60122: Merged V4.2-BUG-FIX (4.2.2) to HEAD-BUG-FIX (Cloud/4.3)
      60121: Merged V4.1-BUG-FIX (4.1.8) to V4.2-BUG-FIX (4.2.2)
         60118: Merged DEV (V4.1-BUG-FIX-2013_11_14) to V4.1-BUG-FIX (4.1.8)
            60117: Merged DEV (V4.2.1-2014_01_09) to DEV (V4.1-BUG-FIX-2013_11_14)
               60116: MNT-10399: AutomaticBuilds: Alfresco doesn't shut down because of FullTextSearchIndexerImpl 
                  - Try to spot dead daemon threads holding the lock on subsystem shutdown


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@62267 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2014-02-12 10:24:55 +00:00
parent bad9fe65ff
commit a1c7917881

View File

@@ -61,6 +61,77 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc
private int batchSize = 1000; 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<Result>
{
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) * @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()) new SynchronizedHelper<Void>()
{ {
s_logger.debug("FTS index request for "+storeRef); public Void run()
} {
requiresIndex.add(storeRef); 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) * @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 new SynchronizedHelper<Void>()
{ {
if(s_logger.isDebugEnabled()) public Void run()
{ {
s_logger.debug("FTS index completed for "+storeRef+" ... "+remaining+ " remaining"); try
} {
indexing.remove(storeRef); if(s_logger.isDebugEnabled())
if ((remaining > 0) || (t != null)) {
{ s_logger.debug("FTS index completed for "+storeRef+" ... "+remaining+ " remaining");
requiresIndex(storeRef); }
} indexing.remove(storeRef);
if (t != null) if ((remaining > 0) || (t != null))
{ {
throw new FTSIndexerException(t); requiresIndex(storeRef);
} }
} if (t != null)
finally {
{ throw new FTSIndexerException(t);
this.notifyAll(); }
} }
finally
{
FullTextSearchIndexerImpl.this.notifyAll();
}
return null;
}
}.executeNoInterruptedException();
} }
/* /*
@@ -120,29 +205,40 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc
*/ */
public synchronized void pause() throws InterruptedException public synchronized void pause() throws InterruptedException
{ {
pauseCount++; new SynchronizedHelper<Void>()
if(s_logger.isTraceEnabled()) {
{ public Void run() throws InterruptedException
s_logger.trace("..Waiting "+pauseCount+" id is "+this); {
} pauseCount++;
while ((indexing.size() > 0)) if(s_logger.isTraceEnabled())
{ {
if(s_logger.isTraceEnabled()) s_logger.trace("..Waiting "+pauseCount+" id is "+this);
{ }
s_logger.trace("Pause: Waiting with count of "+indexing.size()+" id is "+this); while ((indexing.size() > 0))
} {
this.wait(); if(s_logger.isTraceEnabled())
} {
pauseCount--; s_logger.trace("Pause: Waiting with count of "+indexing.size()+" id is "+this);
if (pauseCount == 0) }
{
paused = true; if (waitAndCheckForSubsystemShutdown())
this.notifyAll(); // only resumers {
} indexing.clear();
if(s_logger.isTraceEnabled()) }
{ }
s_logger.trace("..Remaining "+pauseCount +" paused = "+paused+" id is "+this); 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 public synchronized void resume() throws InterruptedException
{ {
if (pauseCount == 0) new SynchronizedHelper<Void>()
{ {
if(s_logger.isTraceEnabled()) public Void run() throws InterruptedException
{ {
s_logger.trace("Direct resume"+" id is "+this); if (pauseCount == 0)
}
paused = false;
}
else
{
while (pauseCount > 0)
{
if(s_logger.isTraceEnabled())
{ {
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(); else
} {
paused = false; 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") @SuppressWarnings("unused")
private synchronized boolean isPaused() throws InterruptedException private synchronized boolean isPaused() throws InterruptedException
{ {
if (pauseCount == 0) return new SynchronizedHelper<Boolean>()
{ {
return paused; public Boolean run() throws InterruptedException
} {
else if (pauseCount == 0)
{ {
while (pauseCount > 0) return paused;
{ }
this.wait(); else
} {
return paused; while (pauseCount > 0)
} {
if (waitAndCheckForSubsystemShutdown())
{
break;
}
}
return paused;
}
}
}.execute();
} }
/* /*
@@ -262,34 +378,40 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc
private synchronized StoreRef getNextRef() private synchronized StoreRef getNextRef()
{ {
if (paused || (pauseCount > 0)) return new SynchronizedHelper<StoreRef>()
{ {
if(s_logger.isTraceEnabled()) public StoreRef run()
{ {
s_logger.trace("Indexing suspended - no store available - id is "+this); if (paused || (pauseCount > 0))
} {
return null; 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) for (StoreRef ref : requiresIndex)
{ {
if (!indexing.contains(ref)) if (!indexing.contains(ref))
{ {
nextStoreRef = ref; nextStoreRef = ref;
// FIFO // FIFO
break; break;
} }
} }
if (nextStoreRef != null) if (nextStoreRef != null)
{ {
requiresIndex.remove(nextStoreRef); requiresIndex.remove(nextStoreRef);
indexing.add(nextStoreRef); indexing.add(nextStoreRef);
} }
return nextStoreRef; return nextStoreRef;
}
}.executeNoInterruptedException();
} }
/** /**
@@ -353,6 +475,4 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc
requiresIndex.clear(); requiresIndex.clear();
indexing.clear(); indexing.clear();
} }
} }