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