Code with Finding: |
class IndexWriter {
/** Walk through all files referenced by the current
* segmentInfos and ask the Directory to sync each file,
* if it wasn't already. If that succeeds, then we
* prepare a new segments_N file but do not fully commit
* it. */
private void startCommit(long sizeInBytes, Map commitUserData) throws IOException {
assert testPoint("startStartCommit");
if (hitOOM) {
throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot commit");
}
try {
if (infoStream != null)
message("startCommit(): start sizeInBytes=" + sizeInBytes);
if (sizeInBytes > 0)
syncPause(sizeInBytes);
SegmentInfos toSync = null;
final long myChangeCount;
synchronized(this) {
// sizeInBytes > 0 means this is an autoCommit at
// the end of a merge. If at this point stopMerges
// is true (which means a rollback() or
// rollbackTransaction() is waiting for us to
// finish), we skip the commit to avoid deadlock
if (sizeInBytes > 0 && stopMerges)
return;
// Wait for any running addIndexes to complete
// first, then block any from running until we've
// copied the segmentInfos we intend to sync:
blockAddIndexes(false);
// On commit the segmentInfos must never
// reference a segment in another directory:
assert !hasExternalSegments();
try {
assert lastCommitChangeCount <= changeCount;
if (changeCount == lastCommitChangeCount) {
if (infoStream != null)
message(" skip startCommit(): no changes pending");
return;
}
// First, we clone & incref the segmentInfos we intend
// to sync, then, without locking, we sync() each file
// referenced by toSync, in the background. Multiple
// threads can be doing this at once, if say a large
// merge and a small merge finish at the same time:
if (infoStream != null)
message("startCommit index=" + segString(segmentInfos) + " changeCount=" + changeCount);
readerPool.commit();
toSync = (SegmentInfos) segmentInfos.clone();
if (commitUserData != null)
toSync.setUserData(commitUserData);
deleter.incRef(toSync, false);
myChangeCount = changeCount;
Iterator it = toSync.files(directory, false).iterator();
while(it.hasNext()) {
String fileName = (String) it.next();
assert directory.fileExists(fileName): "file " + fileName + " does not exist";
}
} finally {
resumeAddIndexes();
}
}
assert testPoint("midStartCommit");
boolean setPending = false;
try {
// Loop until all files toSync references are sync'd:
while(true) {
final Collection pending = new ArrayList();
Iterator it = toSync.files(directory, false).iterator();
while(it.hasNext()) {
final String fileName = (String) it.next();
if (startSync(fileName, pending)) {
boolean success = false;
try {
// Because we incRef'd this commit point, above,
// the file had better exist:
assert directory.fileExists(fileName): "file '" + fileName + "' does not exist dir=" + directory;
if (infoStream != null)
message("now sync " + fileName);
directory.sync(fileName);
success = true;
} finally {
finishSync(fileName, success);
}
}
}
// All files that I require are either synced or being
// synced by other threads. If they are being synced,
// we must at this point block until they are done.
// If this returns false, that means an error in
// another thread resulted in failing to actually
// sync one of our files, so we repeat:
if (waitForAllSynced(pending))
break;
}
assert testPoint("midStartCommit2");
synchronized(this) {
// If someone saved a newer version of segments file
// since I first started syncing my version, I can
// safely skip saving myself since I've been
// superseded:
while(true) {
if (myChangeCount <= lastCommitChangeCount) {
if (infoStream != null) {
message("sync superseded by newer infos");
}
break;
} else if (pendingCommit == null) {
// My turn to commit
if (segmentInfos.getGeneration() > toSync.getGeneration())
toSync.updateGeneration(segmentInfos);
boolean success = false;
try {
// Exception here means nothing is prepared
// (this method unwinds everything it did on
// an exception)
try {
toSync.prepareCommit(directory);
} finally {
// Have our master segmentInfos record the
// generations we just prepared. We do this
// on error or success so we don't
// double-write a segments_N file.
segmentInfos.updateGeneration(toSync);
}
assert pendingCommit == null;
setPending = true;
pendingCommit = toSync;
pendingCommitChangeCount = myChangeCount;
success = true;
} finally {
if (!success && infoStream != null)
message("hit exception committing segments file");
}
break;
} else {
// Must wait for other commit to complete
doWait();
}
}
}
if (infoStream != null)
message("done all syncs");
assert testPoint("midStartCommitSuccess");
} finally {
synchronized(this) {
if (!setPending)
deleter.decRef(toSync);
}
}
} catch (OutOfMemoryError oom) {
handleOOM(oom, "startCommit");
}
assert testPoint("finishStartCommit");
}
}
|