void (final int modCount) { if (betterToInterruptFlushingEarly(modCount)) { return; } IndexingStamp.flushCaches(); final int maxAttemptsPerIndex = 2; final int maxSleepPerAttemptMs = 16; IndexConfiguration state = getState(); int interferencesWithOtherThreads = 0; for (ID<?, ?> indexId : state.getIndexIDs()) { if (betterToInterruptFlushingEarly(modCount)) { return; // do not interfere with 'main' jobs } try { final UpdatableIndex<?, ?, FileContent, ?> index = state.getIndex(indexId); if (index != null) { //RC: regular flush should not interfere with other, (likely) more response-time-critical // jobs. We can't guarantee the total absence of interference, though -- instead we're // trying to be just 'nice to others' here. // I.e. do .yield() after each flush, so somebody who waits for index access -- has its // chance. We also use .tryLock() as a way to feel interference (readLock.tryLock fails // -> somebody else acquired write lock), and back off a bit, giving 'another job' a chance // to finish. // (See e.g., IDEA-244174 for what could go wrong otherwise) for (int attempt = 0; attempt < maxAttemptsPerIndex; attempt++) { final ReadWriteLock rwLock = index.getLock(); final Lock indexReadLock = rwLock.readLock(); final boolean lockSucceeded = indexReadLock.tryLock(); if (lockSucceeded) { try { index.flush(); } finally { indexReadLock.unlock(); } interferencesWithOtherThreads--; break; } else { interferencesWithOtherThreads++; } // linear backoff based on how many times we contended with others: final int toWaitMs = clamp(interferencesWithOtherThreads, 0, maxSleepPerAttemptMs); if (toWaitMs == 0) { Thread.yield(); } else { Thread.sleep(toWaitMs); } } } } catch (Throwable e) { requestRebuild(indexId, e); } } }