Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/nio/ch/FileLockTable.java
38918 views
/*1* Copyright (c) 2005, 2009, 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.nio.channels.*;28import java.util.*;29import java.util.concurrent.ConcurrentHashMap;30import java.lang.ref.*;31import java.io.FileDescriptor;32import java.io.IOException;3334abstract class FileLockTable {35protected FileLockTable() {36}3738/**39* Creates and returns a file lock table for a channel that is connected to40* the a system-wide map of all file locks for the Java virtual machine.41*/42public static FileLockTable newSharedFileLockTable(Channel channel,43FileDescriptor fd)44throws IOException45{46return new SharedFileLockTable(channel, fd);47}4849/**50* Adds a file lock to the table.51*52* @throws OverlappingFileLockException if the file lock overlaps53* with an existing file lock in the table54*/55public abstract void add(FileLock fl) throws OverlappingFileLockException;5657/**58* Remove an existing file lock from the table.59*/60public abstract void remove(FileLock fl);6162/**63* Removes all file locks from the table.64*65* @return The list of file locks removed66*/67public abstract List<FileLock> removeAll();6869/**70* Replaces an existing file lock in the table.71*/72public abstract void replace(FileLock fl1, FileLock fl2);73}747576/**77* A file lock table that is over a system-wide map of all file locks.78*/79class SharedFileLockTable extends FileLockTable {8081/**82* A weak reference to a FileLock.83* <p>84* SharedFileLockTable uses a list of file lock references to avoid keeping the85* FileLock (and FileChannel) alive.86*/87private static class FileLockReference extends WeakReference<FileLock> {88private FileKey fileKey;8990FileLockReference(FileLock referent,91ReferenceQueue<FileLock> queue,92FileKey key) {93super(referent, queue);94this.fileKey = key;95}9697FileKey fileKey() {98return fileKey;99}100}101102// The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.103// The map value is a list of file locks represented by FileLockReferences.104// All access to the list must be synchronized on the list.105private static ConcurrentHashMap<FileKey, List<FileLockReference>> lockMap =106new ConcurrentHashMap<FileKey, List<FileLockReference>>();107108// reference queue for cleared refs109private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();110111// The connection to which this table is connected112private final Channel channel;113114// File key for the file that this channel is connected to115private final FileKey fileKey;116117SharedFileLockTable(Channel channel, FileDescriptor fd) throws IOException {118this.channel = channel;119this.fileKey = FileKey.create(fd);120}121122@Override123public void add(FileLock fl) throws OverlappingFileLockException {124List<FileLockReference> list = lockMap.get(fileKey);125126for (;;) {127128// The key isn't in the map so we try to create it atomically129if (list == null) {130list = new ArrayList<FileLockReference>(2);131List<FileLockReference> prev;132synchronized (list) {133prev = lockMap.putIfAbsent(fileKey, list);134if (prev == null) {135// we successfully created the key so we add the file lock136list.add(new FileLockReference(fl, queue, fileKey));137break;138}139}140// someone else got there first141list = prev;142}143144// There is already a key. It is possible that some other thread145// is removing it so we re-fetch the value from the map. If it146// hasn't changed then we check the list for overlapping locks147// and add the new lock to the list.148synchronized (list) {149List<FileLockReference> current = lockMap.get(fileKey);150if (list == current) {151checkList(list, fl.position(), fl.size());152list.add(new FileLockReference(fl, queue, fileKey));153break;154}155list = current;156}157158}159160// process any stale entries pending in the reference queue161removeStaleEntries();162}163164private void removeKeyIfEmpty(FileKey fk, List<FileLockReference> list) {165assert Thread.holdsLock(list);166assert lockMap.get(fk) == list;167if (list.isEmpty()) {168lockMap.remove(fk);169}170}171172@Override173public void remove(FileLock fl) {174assert fl != null;175176// the lock must exist so the list of locks must be present177List<FileLockReference> list = lockMap.get(fileKey);178if (list == null) return;179180synchronized (list) {181int index = 0;182while (index < list.size()) {183FileLockReference ref = list.get(index);184FileLock lock = ref.get();185if (lock == fl) {186assert (lock != null) && (lock.acquiredBy() == channel);187ref.clear();188list.remove(index);189break;190}191index++;192}193}194}195196@Override197public List<FileLock> removeAll() {198List<FileLock> result = new ArrayList<FileLock>();199List<FileLockReference> list = lockMap.get(fileKey);200if (list != null) {201synchronized (list) {202int index = 0;203while (index < list.size()) {204FileLockReference ref = list.get(index);205FileLock lock = ref.get();206207// remove locks obtained by this channel208if (lock != null && lock.acquiredBy() == channel) {209// remove the lock from the list210ref.clear();211list.remove(index);212213// add to result214result.add(lock);215} else {216index++;217}218}219220// once the lock list is empty we remove it from the map221removeKeyIfEmpty(fileKey, list);222}223}224return result;225}226227@Override228public void replace(FileLock fromLock, FileLock toLock) {229// the lock must exist so there must be a list230List<FileLockReference> list = lockMap.get(fileKey);231assert list != null;232233synchronized (list) {234for (int index=0; index<list.size(); index++) {235FileLockReference ref = list.get(index);236FileLock lock = ref.get();237if (lock == fromLock) {238ref.clear();239list.set(index, new FileLockReference(toLock, queue, fileKey));240break;241}242}243}244}245246// Check for overlapping file locks247private void checkList(List<FileLockReference> list, long position, long size)248throws OverlappingFileLockException249{250assert Thread.holdsLock(list);251for (FileLockReference ref: list) {252FileLock fl = ref.get();253if (fl != null && fl.overlaps(position, size))254throw new OverlappingFileLockException();255}256}257258// Process the reference queue259private void removeStaleEntries() {260FileLockReference ref;261while ((ref = (FileLockReference)queue.poll()) != null) {262FileKey fk = ref.fileKey();263List<FileLockReference> list = lockMap.get(fk);264if (list != null) {265synchronized (list) {266list.remove(ref);267removeKeyIfEmpty(fk, list);268}269}270}271}272}273274275