Path: blob/master/src/java.base/share/classes/sun/nio/ch/FileLockTable.java
41159 views
/*1* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.ch;2627import java.io.FileDescriptor;28import java.io.IOException;29import java.lang.ref.ReferenceQueue;30import java.lang.ref.WeakReference;31import java.nio.channels.Channel;32import java.nio.channels.FileLock;33import java.nio.channels.OverlappingFileLockException;34import java.util.ArrayList;35import java.util.HashSet;36import java.util.List;37import java.util.Set;38import java.util.concurrent.ConcurrentHashMap;3940/**41* A file lock table that is over a system-wide map of all file locks.42*/43class FileLockTable {44/**45* A weak reference to a FileLock.46* <p>47* FileLockTable uses a list of file lock references to avoid keeping the48* FileLock (and FileChannel) alive.49*/50private static class FileLockReference extends WeakReference<FileLock> {51private FileKey fileKey;5253FileLockReference(FileLock referent,54ReferenceQueue<FileLock> queue,55FileKey key) {56super(referent, queue);57this.fileKey = key;58}5960FileKey fileKey() {61return fileKey;62}63}6465// The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.66// The map value is a list of file locks represented by FileLockReferences.67// All access to the list must be synchronized on the list.68private static ConcurrentHashMap<FileKey, List<FileLockReference>> lockMap =69new ConcurrentHashMap<FileKey, List<FileLockReference>>();7071// reference queue for cleared refs72private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();7374// The connection to which this table is connected75private final Channel channel;7677// File key for the file that this channel is connected to78private final FileKey fileKey;7980// Locks obtained for this channel81private final Set<FileLock> locks;8283/**84* Creates a file lock table for a channel that is connected to the85* system-wide map of all file locks for the Java virtual machine.86*/87FileLockTable(Channel channel, FileDescriptor fd) throws IOException {88this.channel = channel;89this.fileKey = FileKey.create(fd);90this.locks = new HashSet<FileLock>();91}9293void add(FileLock fl) throws OverlappingFileLockException {94List<FileLockReference> list = lockMap.get(fileKey);9596for (;;) {9798// The key isn't in the map so we try to create it atomically99if (list == null) {100list = new ArrayList<FileLockReference>(2);101List<FileLockReference> prev;102synchronized (list) {103prev = lockMap.putIfAbsent(fileKey, list);104if (prev == null) {105// we successfully created the key so we add the file lock106list.add(new FileLockReference(fl, queue, fileKey));107locks.add(fl);108break;109}110}111// someone else got there first112list = prev;113}114115// There is already a key. It is possible that some other thread116// is removing it so we re-fetch the value from the map. If it117// hasn't changed then we check the list for overlapping locks118// and add the new lock to the list.119synchronized (list) {120List<FileLockReference> current = lockMap.get(fileKey);121if (list == current) {122checkList(list, fl.position(), fl.size());123list.add(new FileLockReference(fl, queue, fileKey));124locks.add(fl);125break;126}127list = current;128}129130}131132// process any stale entries pending in the reference queue133removeStaleEntries();134}135136private void removeKeyIfEmpty(FileKey fk, List<FileLockReference> list) {137assert Thread.holdsLock(list);138assert lockMap.get(fk) == list;139if (list.isEmpty()) {140lockMap.remove(fk);141}142}143144void remove(FileLock fl) {145assert fl != null;146147// the lock must exist so the list of locks must be present148List<FileLockReference> list = lockMap.get(fileKey);149if (list == null) return;150151synchronized (list) {152int index = 0;153while (index < list.size()) {154FileLockReference ref = list.get(index);155FileLock lock = ref.get();156if (lock == fl) {157assert (lock != null) && (lock.acquiredBy() == channel);158ref.clear();159list.remove(index);160locks.remove(fl);161break;162}163index++;164}165}166}167168List<FileLock> removeAll() {169List<FileLock> result = new ArrayList<FileLock>();170List<FileLockReference> list = lockMap.get(fileKey);171if (list != null) {172synchronized (list) {173int index = 0;174while (index < list.size()) {175FileLockReference ref = list.get(index);176FileLock lock = ref.get();177178// remove locks obtained by this channel179if (lock != null && lock.acquiredBy() == channel) {180// remove the lock from the list181ref.clear();182list.remove(index);183184// add to result185result.add(lock);186} else {187index++;188}189}190191// once the lock list is empty we remove it from the map192removeKeyIfEmpty(fileKey, list);193194locks.clear();195}196}197return result;198}199200void replace(FileLock fromLock, FileLock toLock) {201// the lock must exist so there must be a list202List<FileLockReference> list = lockMap.get(fileKey);203assert list != null;204205synchronized (list) {206for (int index=0; index<list.size(); index++) {207FileLockReference ref = list.get(index);208FileLock lock = ref.get();209if (lock == fromLock) {210ref.clear();211list.set(index, new FileLockReference(toLock, queue, fileKey));212locks.remove(fromLock);213locks.add(toLock);214break;215}216}217}218}219220// Check for overlapping file locks221private void checkList(List<FileLockReference> list, long position, long size)222throws OverlappingFileLockException223{224assert Thread.holdsLock(list);225for (FileLockReference ref: list) {226FileLock fl = ref.get();227if (fl != null && fl.overlaps(position, size))228throw new OverlappingFileLockException();229}230}231232// Process the reference queue233private void removeStaleEntries() {234FileLockReference ref;235while ((ref = (FileLockReference)queue.poll()) != null) {236FileKey fk = ref.fileKey();237List<FileLockReference> list = lockMap.get(fk);238if (list != null) {239synchronized (list) {240list.remove(ref);241removeKeyIfEmpty(fk, list);242}243}244}245}246}247248249