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
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);
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);
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 }
|
1 /*
2 * Copyright (c) 2005, 2018, 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
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 final FileKey fileKey;
90 final long position;
91 final long size;
92 private volatile boolean invalid;
93
94 FileLockReference(FileLock referent,
95 ReferenceQueue<FileLock> queue,
96 FileKey key) {
97 super(referent, queue);
98 this.fileKey = key;
99 this.position = referent.position();
100 this.size = referent.size();
101 }
102
103 FileKey fileKey() {
104 return fileKey;
105 }
106
107 void invalidate() {
108 invalid = true;
109 }
110
111 boolean isValid() {
112 return !invalid;
113 }
114
115 boolean overlaps(long position, long size) {
116 if (position + size <= this.position)
117 return false; // That is below this
118 if (this.position + this.size <= position)
119 return false; // This is below that
120 return true;
121 }
122 }
123
124 // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
125 // The map value is a list of file locks represented by FileLockReferences.
126 // All access to the list must be synchronized on the list.
127 private static ConcurrentHashMap<FileKey, List<FileLockReference>> lockMap =
128 new ConcurrentHashMap<FileKey, List<FileLockReference>>();
129
130 // reference queue for cleared refs
131 private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
132
133 // The connection to which this table is connected
134 private final Channel channel;
135
136 // File key for the file that this channel is connected to
137 private final FileKey fileKey;
138
139 SharedFileLockTable(Channel channel, FileDescriptor fd) throws IOException {
140 this.channel = channel;
141 this.fileKey = FileKey.create(fd);
190 lockMap.remove(fk);
191 }
192 }
193
194 @Override
195 public void remove(FileLock fl) {
196 assert fl != null;
197
198 // the lock must exist so the list of locks must be present
199 List<FileLockReference> list = lockMap.get(fileKey);
200 if (list == null) return;
201
202 synchronized (list) {
203 int index = 0;
204 while (index < list.size()) {
205 FileLockReference ref = list.get(index);
206 FileLock lock = ref.get();
207 if (lock == fl) {
208 assert (lock != null) && (lock.acquiredBy() == channel);
209 ref.clear();
210 ref.invalidate();
211 list.remove(index);
212 break;
213 }
214 index++;
215 }
216 }
217 }
218
219 @Override
220 public List<FileLock> removeAll() {
221 List<FileLock> result = new ArrayList<FileLock>();
222 List<FileLockReference> list = lockMap.get(fileKey);
223 if (list != null) {
224 synchronized (list) {
225 int index = 0;
226 while (index < list.size()) {
227 FileLockReference ref = list.get(index);
228 FileLock lock = ref.get();
229
230 // remove locks obtained by this channel
231 if (lock != null && lock.acquiredBy() == channel) {
232 // remove the lock from the list
233 ref.clear();
234 ref.invalidate();
235 list.remove(index);
236
237 // add to result
238 result.add(lock);
239 } else {
240 index++;
241 }
242 }
243
244 // once the lock list is empty we remove it from the map
245 removeKeyIfEmpty(fileKey, list);
246 }
247 }
248 return result;
249 }
250
251 @Override
252 public void replace(FileLock fromLock, FileLock toLock) {
253 // the lock must exist so there must be a list
254 List<FileLockReference> list = lockMap.get(fileKey);
257 synchronized (list) {
258 for (int index=0; index<list.size(); index++) {
259 FileLockReference ref = list.get(index);
260 FileLock lock = ref.get();
261 if (lock == fromLock) {
262 ref.clear();
263 list.set(index, new FileLockReference(toLock, queue, fileKey));
264 break;
265 }
266 }
267 }
268 }
269
270 // Check for overlapping file locks
271 private void checkList(List<FileLockReference> list, long position, long size)
272 throws OverlappingFileLockException
273 {
274 assert Thread.holdsLock(list);
275 for (FileLockReference ref: list) {
276 FileLock fl = ref.get();
277 if ((fl != null || ref.isValid()) && ref.overlaps(position, size))
278 throw new OverlappingFileLockException();
279 }
280 }
281
282 // Process the reference queue
283 private void removeStaleEntries() {
284 FileLockReference ref;
285 while ((ref = (FileLockReference)queue.poll()) != null) {
286 FileKey fk = ref.fileKey();
287 List<FileLockReference> list = lockMap.get(fk);
288 if (list != null) {
289 synchronized (list) {
290 if (!ref.isValid()) {
291 list.remove(ref);
292 }
293 removeKeyIfEmpty(fk, list);
294 }
295 }
296 }
297 }
298 }
|