Index: lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java =================================================================== --- lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java (revision 1560492) +++ lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java (working copy) @@ -41,8 +41,10 @@ import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.BaseDirectoryWrapper; import org.apache.lucene.store.Directory; +import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.MockDirectoryWrapper; import org.apache.lucene.store.RAMDirectory; +import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util._TestUtil; @@ -1244,4 +1246,27 @@ dest.close(); } + /** Make sure an open IndexWriter on an incoming Directory + * causes a LockObtainFailedException */ + public void testLocksBlock() throws Exception { + Directory src = newDirectory(); + RandomIndexWriter w1 = new RandomIndexWriter(random(), src); + w1.addDocument(new Document()); + w1.commit(); + + Directory dest = newDirectory(); + + IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random())); + iwc.setWriteLockTimeout(1); + RandomIndexWriter w2 = new RandomIndexWriter(random(), dest, iwc); + + try { + w2.addIndexes(src); + fail("did not hit expected exception"); + } catch (LockObtainFailedException lofe) { + // expected + } + + IOUtils.close(w1, w2, src, dest); + } } Index: lucene/core/src/test/org/apache/lucene/index/TestIndexWriterReader.java =================================================================== --- lucene/core/src/test/org/apache/lucene/index/TestIndexWriterReader.java (revision 1560492) +++ lucene/core/src/test/org/apache/lucene/index/TestIndexWriterReader.java (working copy) @@ -730,8 +730,10 @@ final long endTime = (long) (System.currentTimeMillis() + 1000.*SECONDS); final List excs = Collections.synchronizedList(new ArrayList()); - final Thread[] threads = new Thread[numThreads]; - for(int i=0;iTypical use might look like:
  * new Lock.With(directory.makeLock("my.lock")) {
@@ -31,7 +33,7 @@
  *
  * @see Directory#makeLock(String)
  */
-public abstract class Lock {
+public abstract class Lock implements Closeable {
 
   /** How long {@link #obtain(long)} waits, in milliseconds,
    *  in between attempts to acquire the lock. */
@@ -98,7 +100,7 @@
   }
 
   /** Releases exclusive access. */
-  public abstract void release() throws IOException;
+  public abstract void close() throws IOException;
 
   /** Returns true if the resource is currently locked.  Note that one must
    * still call {@link #obtain()} before using the resource. */
@@ -134,8 +136,9 @@
          locked = lock.obtain(lockWaitTimeout);
          return doBody();
       } finally {
-        if (locked)
-          lock.release();
+        if (locked) {
+          lock.close();
+        }
       }
     }
   }
Index: lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java	(revision 1560492)
+++ lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java	(working copy)
@@ -71,7 +71,7 @@
   }
 
   @Override
-  public void release() {
+  public void close() {
     synchronized(locks) {
       locks.remove(lockName);
     }
Index: lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java	(revision 1560492)
+++ lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java	(working copy)
@@ -55,7 +55,7 @@
   }
 
   @Override
-  public void release() {
+  public void close() {
   }
 
   @Override
Index: lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java	(revision 1560492)
+++ lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java	(working copy)
@@ -128,9 +128,10 @@
   }
 
   @Override
-  public void release() throws LockReleaseFailedException {
-    if (lockFile.exists() && !lockFile.delete())
+  public void close() throws LockReleaseFailedException {
+    if (lockFile.exists() && !lockFile.delete()) {
       throw new LockReleaseFailedException("failed to delete " + lockFile);
+    }
   }
 
   @Override
Index: lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/IndexWriter.java	(revision 1560492)
+++ lucene/core/src/java/org/apache/lucene/index/IndexWriter.java	(working copy)
@@ -795,11 +795,7 @@
         if (infoStream.isEnabled("IW")) {
           infoStream.message("IW", "init: hit exception on init; releasing write lock");
         }
-        try {
-          writeLock.release();
-        } catch (Throwable t) {
-          // don't mask the original exception
-        }
+        IOUtils.closeWhileHandlingException(writeLock);
         writeLock = null;
       }
     }
@@ -1052,7 +1048,7 @@
       }
 
       if (writeLock != null) {
-        writeLock.release();                          // release write lock
+        writeLock.close();                          // release write lock
         writeLock = null;
       }
       synchronized(this) {
@@ -2362,6 +2358,28 @@
     }
   }
 
+  /** Acquires write locks on all the Directories; be sure
+   *  to match with a call to {@link IOUtils#close} in a
+   *  finally clause. */
+  private List acquireWriteLocks(Directory... dirs) throws IOException {
+    List locks = new ArrayList();
+    for(int i=0;i
-   * NOTE: the index in each {@link Directory} must not be
-   * changed (opened by a writer) while this method is
-   * running.  This method does not acquire a write lock in
-   * each input Directory, so it is up to the caller to
-   * enforce this.
+   * NOTE: this method acquires the write lock in
+   * each directory, to ensure that no {@code IndexWriter}
+   * is currently open or tries to open while this is
+   * running.
    *
    * 

This method is transactional in how Exceptions are * handled: it does not commit a new segments_N file until @@ -2405,12 +2422,18 @@ * * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws LockObtainFailedException if we were unable to + * acquire the write lock in at least one directory */ public void addIndexes(Directory... dirs) throws IOException { ensureOpen(); noDupDirs(dirs); + List locks = acquireWriteLocks(dirs); + + boolean successTop = false; + try { if (infoStream.isEnabled("IW")) { infoStream.message("IW", "flush at addIndexes(Directory...)"); @@ -2481,8 +2504,17 @@ checkpoint(); } + successTop = true; + } catch (OutOfMemoryError oom) { + IOUtils.closeWhileHandlingException(locks); handleOOM(oom, "addIndexes(Directory...)"); + } finally { + if (successTop) { + IOUtils.close(locks); + } else { + IOUtils.closeWhileHandlingException(locks); + } } } @@ -4415,7 +4447,7 @@ * currently accessing this index. */ public static void unlock(Directory directory) throws IOException { - directory.makeLock(IndexWriter.WRITE_LOCK_NAME).release(); + directory.makeLock(IndexWriter.WRITE_LOCK_NAME).close(); } /** If {@link DirectoryReader#open(IndexWriter,boolean)} has Index: lucene/test-framework/src/java/org/apache/lucene/store/MockLockFactoryWrapper.java =================================================================== --- lucene/test-framework/src/java/org/apache/lucene/store/MockLockFactoryWrapper.java (revision 1560492) +++ lucene/test-framework/src/java/org/apache/lucene/store/MockLockFactoryWrapper.java (working copy) @@ -78,8 +78,8 @@ } @Override - public void release() throws IOException { - delegateLock.release(); + public void close() throws IOException { + delegateLock.close(); dir.openLocks.remove(name); }