< 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, 2016, 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

@@ -85,21 +85,29 @@
      * SharedFileLockTable uses a list of file lock references to avoid keeping the
      * FileLock (and FileChannel) alive.
      */
     private static class FileLockReference extends WeakReference<FileLock> {
         private FileKey fileKey;
+        private FileLockState lockState;
 
         FileLockReference(FileLock referent,
                           ReferenceQueue<FileLock> queue,
                           FileKey key) {
             super(referent, queue);
             this.fileKey = key;
+            if (referent instanceof FileLockImpl) {
+                this.lockState = ((FileLockImpl)referent).getState();
+            }
         }
 
         FileKey fileKey() {
             return fileKey;
         }
+
+        FileLockState lockState() {
+            return lockState;
+        }
     }
 
     // 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.

@@ -107,10 +115,16 @@
         new ConcurrentHashMap<FileKey, List<FileLockReference>>();
 
     // reference queue for cleared refs
     private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
 
+    // System-wide map of orphaned lock states. A lock state is orphaned when
+    // the corresponding FileLock is no longer reachable but the underlying
+    // native lock has not been released.
+    private static ConcurrentHashMap<FileKey, List<FileLockState>> orphans =
+        new ConcurrentHashMap<FileKey, List<FileLockState>>();
+
     // The connection to which this table is connected
     private final Channel channel;
 
     // File key for the file that this channel is connected to
     private final FileKey fileKey;

@@ -168,10 +182,17 @@
         if (list.isEmpty()) {
             lockMap.remove(fk);
         }
     }
 
+    private void removeKeyIfStatesEmpty(FileKey fk, List<FileLockState> list) {
+        assert orphans.get(fk) == list;
+        if (list.isEmpty()) {
+            orphans.remove(fk);
+        }
+    }
+
     @Override
     public void remove(FileLock fl) {
         assert fl != null;
 
         // the lock must exist so the list of locks must be present

@@ -248,13 +269,33 @@
     private void checkList(List<FileLockReference> list, long position, long size)
         throws OverlappingFileLockException
     {
         assert Thread.holdsLock(list);
         for (FileLockReference ref: list) {
-            FileLock fl = ref.get();
-            if (fl != null && fl.overlaps(position, size))
+            FileLockState lockState= ref.lockState();
+            if (lockState != null && lockState.isValid()
+                && lockState.overlaps(position, size))
+                throw new OverlappingFileLockException();
+        }
+
+        // Check any orphaned states for this key.
+        List<FileLockState> states = orphans.get(fileKey);
+        if (states != null) {
+            synchronized (states) {
+                Iterator<FileLockState> iter = states.iterator();
+                while (iter.hasNext()) {
+                    FileLockState lockState = iter.next();
+                    if (lockState.isValid()) {
+                        if (lockState.overlaps(position, size))
                 throw new OverlappingFileLockException();
+                    } else {
+                        // Might as well remove invalid state.
+                        iter.remove();
+                    }
+                }
+                removeKeyIfStatesEmpty(fileKey, states);
+            }
         }
     }
 
     // Process the reference queue
     private void removeStaleEntries() {

@@ -264,10 +305,51 @@
             List<FileLockReference> list = lockMap.get(fk);
             if (list != null) {
                 synchronized (list) {
                     list.remove(ref);
                     removeKeyIfEmpty(fk, list);
+                    FileLockState lockState = ref.lockState();
+                    if (lockState == null)
+                        continue;
+                    if (lockState.isValid()) {
+                        // The native lock is still valid although the
+                        // FileLock instance is no longer reachable. Add
+                        // the state to the list of orphans for this key.
+                        // The FileChannel of the referent is unknown.
+                        List<FileLockState> states = orphans.get(fk);
+                        while (true) {
+                            if (states == null) {
+                                states = new ArrayList<FileLockState>(2);
+                                List<FileLockState> prev;
+                                synchronized (states) {
+                                    prev = orphans.putIfAbsent(fk, states);
+                                    if (prev == null) {
+                                        states.add(lockState);
+                                        break;
+                                    }
+                                }
+                                states = prev;
+                            }
+
+                            synchronized (states) {
+                                List<FileLockState> current = orphans.get(fk);
+                                if (states == current) {
+                                    states.add(lockState);
+                                    break;
+                                }
+                                states = current;
+                            }
+                        }
+                    } else { // !lockState.isValid()
+                        List<FileLockState> states = orphans.get(fk);
+                        if (states != null) {
+                            synchronized (states) {
+                                states.remove(lockState);
+                                removeKeyIfStatesEmpty(fk, states);
+                            }
+                        }
+                    }
                 }
             }
         }
     }
 }
< prev index next >