History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/nio/ch/FileLockTable.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/nio/ch/FileLockTable.java+273−0
@@ -0,0 +1,273 @@
1+/*
2+ * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.nio.ch;
27+
28+import java.nio.channels.*;
29+import java.util.*;
30+import java.util.concurrent.ConcurrentHashMap;
31+import java.lang.ref.*;
32+import java.io.FileDescriptor;
33+import java.io.IOException;
34+
35+abstract class FileLockTable {
36+ protected FileLockTable() {
37+ }
38+
39+ /**
40+ * Creates and returns a file lock table for a channel that is connected to
41+ * the a system-wide map of all file locks for the Java virtual machine.
42+ */
43+ public static FileLockTable newSharedFileLockTable(Channel channel,
44+ FileDescriptor fd)
45+ throws IOException
46+ {
47+ return new SharedFileLockTable(channel, fd);
48+ }
49+
50+ /**
51+ * Adds a file lock to the table.
52+ *
53+ * @throws OverlappingFileLockException if the file lock overlaps
54+ * with an existing file lock in the table
55+ */
56+ public abstract void add(FileLock fl) throws OverlappingFileLockException;
57+
58+ /**
59+ * Remove an existing file lock from the table.
60+ */
61+ public abstract void remove(FileLock fl);
62+
63+ /**
64+ * Removes all file locks from the table.
65+ *
66+ * @return The list of file locks removed
67+ */
68+ public abstract List<FileLock> removeAll();
69+
70+ /**
71+ * Replaces an existing file lock in the table.
72+ */
73+ public abstract void replace(FileLock fl1, FileLock fl2);
74+}
75+
76+
77+/**
78+ * A file lock table that is over a system-wide map of all file locks.
79+ */
80+class SharedFileLockTable extends FileLockTable {
81+
82+ /**
83+ * A weak reference to a FileLock.
84+ * <p>
85+ * SharedFileLockTable uses a list of file lock references to avoid keeping the
86+ * FileLock (and FileChannel) alive.
87+ */
88+ private static class FileLockReference extends WeakReference<FileLock> {
89+ private FileKey fileKey;
90+
91+ FileLockReference(FileLock referent,
92+ ReferenceQueue<FileLock> queue,
93+ FileKey key) {
94+ super(referent, queue);
95+ this.fileKey = key;
96+ }
97+
98+ FileKey fileKey() {
99+ return fileKey;
100+ }
101+ }
102+
103+ // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
104+ // The map value is a list of file locks represented by FileLockReferences.
105+ // All access to the list must be synchronized on the list.
106+ private static ConcurrentHashMap<FileKey, List<FileLockReference>> lockMap =
107+ new ConcurrentHashMap<FileKey, List<FileLockReference>>();
108+
109+ // reference queue for cleared refs
110+ private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
111+
112+ // The connection to which this table is connected
113+ private final Channel channel;
114+
115+ // File key for the file that this channel is connected to
116+ private final FileKey fileKey;
117+
118+ SharedFileLockTable(Channel channel, FileDescriptor fd) throws IOException {
119+ this.channel = channel;
120+ this.fileKey = FileKey.create(fd);
121+ }
122+
123+ @Override
124+ public void add(FileLock fl) throws OverlappingFileLockException {
125+ List<FileLockReference> list = lockMap.get(fileKey);
126+
127+ for (;;) {
128+
129+ // The key isn't in the map so we try to create it atomically
130+ if (list == null) {
131+ list = new ArrayList<FileLockReference>(2);
132+ List<FileLockReference> prev;
133+ synchronized (list) {
134+ prev = lockMap.putIfAbsent(fileKey, list);
135+ if (prev == null) {
136+ // we successfully created the key so we add the file lock
137+ list.add(new FileLockReference(fl, queue, fileKey));
138+ break;
139+ }
140+ }
141+ // someone else got there first
142+ list = prev;
143+ }
144+
145+ // There is already a key. It is possible that some other thread
146+ // is removing it so we re-fetch the value from the map. If it
147+ // hasn't changed then we check the list for overlapping locks
148+ // and add the new lock to the list.
149+ synchronized (list) {
150+ List<FileLockReference> current = lockMap.get(fileKey);
151+ if (list == current) {
152+ checkList(list, fl.position(), fl.size());
153+ list.add(new FileLockReference(fl, queue, fileKey));
154+ break;
155+ }
156+ list = current;
157+ }
158+
159+ }
160+
161+ // process any stale entries pending in the reference queue
162+ removeStaleEntries();
163+ }
164+
165+ private void removeKeyIfEmpty(FileKey fk, List<FileLockReference> list) {
166+ assert Thread.holdsLock(list);
167+ assert lockMap.get(fk) == list;
168+ if (list.isEmpty()) {
169+ lockMap.remove(fk);
170+ }
171+ }
172+
173+ @Override
174+ public void remove(FileLock fl) {
175+ assert fl != null;
176+
177+ // the lock must exist so the list of locks must be present
178+ List<FileLockReference> list = lockMap.get(fileKey);
179+ if (list == null) return;
180+
181+ synchronized (list) {
182+ int index = 0;
183+ while (index < list.size()) {
184+ FileLockReference ref = list.get(index);
185+ FileLock lock = ref.get();
186+ if (lock == fl) {
187+ assert (lock != null) && (lock.acquiredBy() == channel);
188+ ref.clear();
189+ list.remove(index);
190+ break;
191+ }
192+ index++;
193+ }
194+ }
195+ }
196+
197+ @Override
198+ public List<FileLock> removeAll() {
199+ List<FileLock> result = new ArrayList<FileLock>();
200+ List<FileLockReference> list = lockMap.get(fileKey);
201+ if (list != null) {
202+ synchronized (list) {
203+ int index = 0;
204+ while (index < list.size()) {
205+ FileLockReference ref = list.get(index);
206+ FileLock lock = ref.get();
207+
208+ // remove locks obtained by this channel
209+ if (lock != null && lock.acquiredBy() == channel) {
210+ // remove the lock from the list
211+ ref.clear();
212+ list.remove(index);
213+
214+ // add to result
215+ result.add(lock);
216+ } else {
217+ index++;
218+ }
219+ }
220+
221+ // once the lock list is empty we remove it from the map
222+ removeKeyIfEmpty(fileKey, list);
223+ }
224+ }
225+ return result;
226+ }
227+
228+ @Override
229+ public void replace(FileLock fromLock, FileLock toLock) {
230+ // the lock must exist so there must be a list
231+ List<FileLockReference> list = lockMap.get(fileKey);
232+ assert list != null;
233+
234+ synchronized (list) {
235+ for (int index=0; index<list.size(); index++) {
236+ FileLockReference ref = list.get(index);
237+ FileLock lock = ref.get();
238+ if (lock == fromLock) {
239+ ref.clear();
240+ list.set(index, new FileLockReference(toLock, queue, fileKey));
241+ break;
242+ }
243+ }
244+ }
245+ }
246+
247+ // Check for overlapping file locks
248+ private void checkList(List<FileLockReference> list, long position, long size)
249+ throws OverlappingFileLockException
250+ {
251+ assert Thread.holdsLock(list);
252+ for (FileLockReference ref: list) {
253+ FileLock fl = ref.get();
254+ if (fl != null && fl.overlaps(position, size))
255+ throw new OverlappingFileLockException();
256+ }
257+ }
258+
259+ // Process the reference queue
260+ private void removeStaleEntries() {
261+ FileLockReference ref;
262+ while ((ref = (FileLockReference)queue.poll()) != null) {
263+ FileKey fk = ref.fileKey();
264+ List<FileLockReference> list = lockMap.get(fk);
265+ if (list != null) {
266+ synchronized (list) {
267+ list.remove(ref);
268+ removeKeyIfEmpty(fk, list);
269+ }
270+ }
271+ }
272+ }
273+}