< prev index next >

src/java.base/share/classes/sun/nio/ch/FileLockTable.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -23,16 +23,20 @@
  * questions.
  */
 
 package sun.nio.ch;
 
-import java.nio.channels.*;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.lang.ref.*;
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.nio.channels.Channel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 
 abstract class FileLockTable {
     protected FileLockTable() {
     }
 

@@ -83,23 +87,54 @@
      * A weak reference to a FileLock.
      * <p>
      * SharedFileLockTable uses a list of file lock references to avoid keeping the
      * FileLock (and FileChannel) alive.
      */
-    private static class FileLockReference extends WeakReference<FileLock> {
+    private static class FileLockReference extends WeakReference<FileLock>
+        implements FileLockListener {
         private FileKey fileKey;
 
+        // Mirror of FileLock state for use when the lock object is no longer
+        // weakly reachable but the native lock has not yet been released.
+        private final long position;
+        private final long size;
+        private boolean isLockReleased;
+
         FileLockReference(FileLock referent,
                           ReferenceQueue<FileLock> queue,
                           FileKey key) {
             super(referent, queue);
             this.fileKey = key;
+            this.position = referent.position();
+            this.size = referent.size();
+            this.isLockReleased = !referent.isValid();
+            if (referent instanceof FileLockImpl) {
+                ((FileLockImpl)referent).setFileLockListener(this);
+            }
         }
 
         FileKey fileKey() {
             return fileKey;
         }
+
+        // FileLockListener implementation
+        public void invalidate() {
+            this.isLockReleased = true;
+        }
+
+        boolean isLockReleased() {
+            return this.isLockReleased;
+        }
+
+        // Copy of FileLock.overlaps(long,long)
+        boolean overlaps(long position, long size) {
+            if (position + size <= this.position)
+                return false;               // That is below this
+            if (this.position + this.size <= position)
+                return false;               // This is below that
+            return true;
+        }
     }
 
     // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
     // The map value is a list of file locks represented by FileLockReferences.
     // All access to the list must be synchronized on the list.

@@ -211,10 +246,13 @@
                         ref.clear();
                         list.remove(index);
 
                         // add to result
                         result.add(lock);
+                    } else if (lock == null && !ref.isLockReleased()) {
+                        ref.clear();
+                        list.remove(index);
                     } else {
                         index++;
                     }
                 }
 

@@ -249,24 +287,32 @@
         throws OverlappingFileLockException
     {
         assert Thread.holdsLock(list);
         for (FileLockReference ref: list) {
             FileLock fl = ref.get();
-            if (fl != null && fl.overlaps(position, size))
+            // Check for overlap if the FileLock instance has not been collected
+            // or the underlying lock has not been released to the file system.
+            if ((fl != null || !ref.isLockReleased())
+                && ref.overlaps(position, size)) {
                 throw new OverlappingFileLockException();
         }
     }
+    }
 
     // Process the reference queue
     private void removeStaleEntries() {
         FileLockReference ref;
         while ((ref = (FileLockReference)queue.poll()) != null) {
             FileKey fk = ref.fileKey();
             List<FileLockReference> list = lockMap.get(fk);
             if (list != null) {
                 synchronized (list) {
+                    // Retain the reference in the list if it refers to a
+                    // FileLock which was collected without being released.
+                    if (ref.isLockReleased()) {
                     list.remove(ref);
+                    }
                     removeKeyIfEmpty(fk, list);
                 }
             }
         }
     }
< prev index next >