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