1 /*
2 * Copyright 2000-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package sun.nio.ch;
27
28 import java.io.FileDescriptor;
29 import java.io.FileInputStream;
30 import java.io.FileOutputStream;
31 import java.io.RandomAccessFile;
32 import java.io.IOException;
33 import java.nio.ByteBuffer;
34 import java.nio.MappedByteBuffer;
35 import java.nio.channels;
36 import java.nio.channels.spi;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Iterator;
40 import java.util.concurrent.ConcurrentHashMap;
41 import java.lang.ref.WeakReference;
42 import java.lang.ref.ReferenceQueue;
43 import java.lang.reflect.Field;
44 import java.security.AccessController;
45 import java.security.PrivilegedAction;
46 import sun.misc.Cleaner;
47 import sun.security.action.GetPropertyAction;
48
49
50 public class FileChannelImpl
51 extends FileChannel
52 {
53
54 // Used to make native read and write calls
55 private static final NativeDispatcher nd;
56
57 // Memory allocation size for mapping buffers
58 private static final long allocationGranularity;
59
60 // Cached field for MappedByteBuffer.isAMappedBuffer
61 private static final Field isAMappedBufferField;
62
63 // File descriptor
64 private final FileDescriptor fd;
65
66 // File access mode (immutable)
67 private final boolean writable;
68 private final boolean readable;
69
70 // Required to prevent finalization of creating stream (immutable)
71 private final Object parent;
72
73 // Thread-safe set of IDs of native threads, for signalling
74 private final NativeThreadSet threads = new NativeThreadSet(2);
75
76 // Lock for operations involving position and size
77 private final Object positionLock = new Object();
78
79 private FileChannelImpl(FileDescriptor fd, boolean readable,
80 boolean writable, Object parent)
81 {
82 this.fd = fd;
83 this.readable = readable;
84 this.writable = writable;
85 this.parent = parent;
86 }
87
88 // Invoked by getChannel() methods
89 // of java.io.File{Input,Output}Stream and RandomAccessFile
90 //
91 public static FileChannel open(FileDescriptor fd,
92 boolean readable, boolean writable,
93 Object parent)
94 {
95 return new FileChannelImpl(fd, readable, writable, parent);
96 }
97
98 private void ensureOpen() throws IOException {
99 if (!isOpen())
100 throw new ClosedChannelException();
101 }
102
103
104 // -- Standard channel operations --
105
106 protected void implCloseChannel() throws IOException {
107
108 nd.preClose(fd);
109 threads.signal();
110
111 // Invalidate and release any locks that we still hold
112 if (fileLockTable != null) {
113 fileLockTable.removeAll( new FileLockTable.Releaser() {
114 public void release(FileLock fl) throws IOException {
115 ((FileLockImpl)fl).invalidate();
116 release0(fd, fl.position(), fl.size());
117 }
118 });
119 }
120
121 if (parent != null) {
122
123 // Close the fd via the parent stream's close method. The parent
124 // will reinvoke our close method, which is defined in the
125 // superclass AbstractInterruptibleChannel, but the isOpen logic in
126 // that method will prevent this method from being reinvoked.
127 //
128 ((java.io.Closeable)parent).close();
129 } else {
130 nd.close(fd);
131 }
132
133 }
134
135 public int read(ByteBuffer dst) throws IOException {
136 ensureOpen();
137 if (!readable)
138 throw new NonReadableChannelException();
139 synchronized (positionLock) {
140 int n = 0;
141 int ti = -1;
142 try {
143 begin();
144 if (!isOpen())
145 return 0;
146 ti = threads.add();
147 do {
148 n = IOUtil.read(fd, dst, -1, nd, positionLock);
149 } while ((n == IOStatus.INTERRUPTED) && isOpen());
150 return IOStatus.normalize(n);
151 } finally {
152 threads.remove(ti);
153 end(n > 0);
154 assert IOStatus.check(n);
155 }
156 }
157 }
158
159 private long read0(ByteBuffer[] dsts) throws IOException {
160 ensureOpen();
161 if (!readable)
162 throw new NonReadableChannelException();
163 synchronized (positionLock) {
164 long n = 0;
165 int ti = -1;
166 try {
167 begin();
168 if (!isOpen())
169 return 0;
170 ti = threads.add();
171 do {
172 n = IOUtil.read(fd, dsts, nd);
173 } while ((n == IOStatus.INTERRUPTED) && isOpen());
174 return IOStatus.normalize(n);
175 } finally {
176 threads.remove(ti);
177 end(n > 0);
178 assert IOStatus.check(n);
179 }
180 }
181 }
182
183 public long read(ByteBuffer[] dsts, int offset, int length)
184 throws IOException
185 {
186 if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
187 throw new IndexOutOfBoundsException();
188 // ## Fix IOUtil.write so that we can avoid this array copy
189 return read0(Util.subsequence(dsts, offset, length));
190 }
191
192 public int write(ByteBuffer src) throws IOException {
193 ensureOpen();
194 if (!writable)
195 throw new NonWritableChannelException();
196 synchronized (positionLock) {
197 int n = 0;
198 int ti = -1;
199 try {
200 begin();
201 if (!isOpen())
202 return 0;
203 ti = threads.add();
204 do {
205 n = IOUtil.write(fd, src, -1, nd, positionLock);
206 } while ((n == IOStatus.INTERRUPTED) && isOpen());
207 return IOStatus.normalize(n);
208 } finally {
209 threads.remove(ti);
210 end(n > 0);
211 assert IOStatus.check(n);
212 }
213 }
214 }
215
216 private long write0(ByteBuffer[] srcs) throws IOException {
217 ensureOpen();
218 if (!writable)
219 throw new NonWritableChannelException();
220 synchronized (positionLock) {
221 long n = 0;
222 int ti = -1;
223 try {
224 begin();
225 if (!isOpen())
226 return 0;
227 ti = threads.add();
228 do {
229 n = IOUtil.write(fd, srcs, nd);
230 } while ((n == IOStatus.INTERRUPTED) && isOpen());
231 return IOStatus.normalize(n);
232 } finally {
233 threads.remove(ti);
234 end(n > 0);
235 assert IOStatus.check(n);
236 }
237 }
238 }
239
240 public long write(ByteBuffer[] srcs, int offset, int length)
241 throws IOException
242 {
243 if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
244 throw new IndexOutOfBoundsException();
245 // ## Fix IOUtil.write so that we can avoid this array copy
246 return write0(Util.subsequence(srcs, offset, length));
247 }
248
249
250 // -- Other operations --
251
252 public long position() throws IOException {
253 ensureOpen();
254 synchronized (positionLock) {
255 long p = -1;
256 int ti = -1;
257 try {
258 begin();
259 if (!isOpen())
260 return 0;
261 ti = threads.add();
262 do {
263 p = position0(fd, -1);
264 } while ((p == IOStatus.INTERRUPTED) && isOpen());
265 return IOStatus.normalize(p);
266 } finally {
267 threads.remove(ti);
268 end(p > -1);
269 assert IOStatus.check(p);
270 }
271 }
272 }
273
274 public FileChannel position(long newPosition) throws IOException {
275 ensureOpen();
276 if (newPosition < 0)
277 throw new IllegalArgumentException();
278 synchronized (positionLock) {
279 long p = -1;
280 int ti = -1;
281 try {
282 begin();
283 if (!isOpen())
284 return null;
285 ti = threads.add();
286 do {
287 p = position0(fd, newPosition);
288 } while ((p == IOStatus.INTERRUPTED) && isOpen());
289 return this;
290 } finally {
291 threads.remove(ti);
292 end(p > -1);
293 assert IOStatus.check(p);
294 }
295 }
296 }
297
298 public long size() throws IOException {
299 ensureOpen();
300 synchronized (positionLock) {
301 long s = -1;
302 int ti = -1;
303 try {
304 begin();
305 if (!isOpen())
306 return -1;
307 ti = threads.add();
308 do {
309 s = size0(fd);
310 } while ((s == IOStatus.INTERRUPTED) && isOpen());
311 return IOStatus.normalize(s);
312 } finally {
313 threads.remove(ti);
314 end(s > -1);
315 assert IOStatus.check(s);
316 }
317 }
318 }
319
320 public FileChannel truncate(long size) throws IOException {
321 ensureOpen();
322 if (size < 0)
323 throw new IllegalArgumentException();
324 if (size > size())
325 return this;
326 if (!writable)
327 throw new NonWritableChannelException();
328 synchronized (positionLock) {
329 int rv = -1;
330 long p = -1;
331 int ti = -1;
332 try {
333 begin();
334 if (!isOpen())
335 return null;
336 ti = threads.add();
337
338 // get current position
339 do {
340 p = position0(fd, -1);
341 } while ((p == IOStatus.INTERRUPTED) && isOpen());
342 if (!isOpen())
343 return null;
344 assert p >= 0;
345
346 // truncate file
347 do {
348 rv = truncate0(fd, size);
349 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
350 if (!isOpen())
351 return null;
352
353 // set position to size if greater than size
354 if (p > size)
355 p = size;
356 do {
357 rv = (int)position0(fd, p);
358 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
359 return this;
360 } finally {
361 threads.remove(ti);
362 end(rv > -1);
363 assert IOStatus.check(rv);
364 }
365 }
366 }
367
368 public void force(boolean metaData) throws IOException {
369 ensureOpen();
370 int rv = -1;
371 int ti = -1;
372 try {
373 begin();
374 if (!isOpen())
375 return;
376 ti = threads.add();
377 do {
378 rv = force0(fd, metaData);
379 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
380 } finally {
381 threads.remove(ti);
382 end(rv > -1);
383 assert IOStatus.check(rv);
384 }
385 }
386
387 // Assume at first that the underlying kernel supports sendfile();
388 // set this to false if we find out later that it doesn't
389 //
390 private static volatile boolean transferSupported = true;
391
392 // Assume that the underlying kernel sendfile() will work if the target
393 // fd is a pipe; set this to false if we find out later that it doesn't
394 //
395 private static volatile boolean pipeSupported = true;
396
397 // Assume that the underlying kernel sendfile() will work if the target
398 // fd is a file; set this to false if we find out later that it doesn't
399 //
400 private static volatile boolean fileSupported = true;
401
402 private long transferToDirectly(long position, int icount,
403 WritableByteChannel target)
404 throws IOException
405 {
406 if (!transferSupported)
407 return IOStatus.UNSUPPORTED;
408
409 FileDescriptor targetFD = null;
410 if (target instanceof FileChannelImpl) {
411 if (!fileSupported)
412 return IOStatus.UNSUPPORTED_CASE;
413 targetFD = ((FileChannelImpl)target).fd;
414 } else if (target instanceof SelChImpl) {
415 // Direct transfer to pipe causes EINVAL on some configurations
416 if ((target instanceof SinkChannelImpl) && !pipeSupported)
417 return IOStatus.UNSUPPORTED_CASE;
418 targetFD = ((SelChImpl)target).getFD();
419 }
420 if (targetFD == null)
421 return IOStatus.UNSUPPORTED;
422 int thisFDVal = IOUtil.fdVal(fd);
423 int targetFDVal = IOUtil.fdVal(targetFD);
424 if (thisFDVal == targetFDVal) // Not supported on some configurations
425 return IOStatus.UNSUPPORTED;
426
427 long n = -1;
428 int ti = -1;
429 try {
430 begin();
431 if (!isOpen())
432 return -1;
433 ti = threads.add();
434 do {
435 n = transferTo0(thisFDVal, position, icount, targetFDVal);
436 } while ((n == IOStatus.INTERRUPTED) && isOpen());
437 if (n == IOStatus.UNSUPPORTED_CASE) {
438 if (target instanceof SinkChannelImpl)
439 pipeSupported = false;
440 if (target instanceof FileChannelImpl)
441 fileSupported = false;
442 return IOStatus.UNSUPPORTED_CASE;
443 }
444 if (n == IOStatus.UNSUPPORTED) {
445 // Don't bother trying again
446 transferSupported = false;
447 return IOStatus.UNSUPPORTED;
448 }
449 return IOStatus.normalize(n);
450 } finally {
451 threads.remove(ti);
452 end (n > -1);
453 }
454 }
455
456 private long transferToTrustedChannel(long position, int icount,
457 WritableByteChannel target)
458 throws IOException
459 {
460 if ( !((target instanceof FileChannelImpl)
461 || (target instanceof SelChImpl)))
462 return IOStatus.UNSUPPORTED;
463
464 // Trusted target: Use a mapped buffer
465 MappedByteBuffer dbb = null;
466 try {
467 dbb = map(MapMode.READ_ONLY, position, icount);
468 // ## Bug: Closing this channel will not terminate the write
469 return target.write(dbb);
470 } finally {
471 if (dbb != null)
472 unmap(dbb);
473 }
474 }
475
476 private long transferToArbitraryChannel(long position, int icount,
477 WritableByteChannel target)
478 throws IOException
479 {
480 // Untrusted target: Use a newly-erased buffer
481 int c = Math.min(icount, TRANSFER_SIZE);
482 ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
483 long tw = 0; // Total bytes written
484 long pos = position;
485 try {
486 Util.erase(bb);
487 while (tw < icount) {
488 bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE));
489 int nr = read(bb, pos);
490 if (nr <= 0)
491 break;
492 bb.flip();
493 // ## Bug: Will block writing target if this channel
494 // ## is asynchronously closed
495 int nw = target.write(bb);
496 tw += nw;
497 if (nw != nr)
498 break;
499 pos += nw;
500 bb.clear();
501 }
502 return tw;
503 } catch (IOException x) {
504 if (tw > 0)
505 return tw;
506 throw x;
507 } finally {
508 Util.releaseTemporaryDirectBuffer(bb);
509 }
510 }
511
512 public long transferTo(long position, long count,
513 WritableByteChannel target)
514 throws IOException
515 {
516 ensureOpen();
517 if (!target.isOpen())
518 throw new ClosedChannelException();
519 if (!readable)
520 throw new NonReadableChannelException();
521 if (target instanceof FileChannelImpl &&
522 !((FileChannelImpl)target).writable)
523 throw new NonWritableChannelException();
524 if ((position < 0) || (count < 0))
525 throw new IllegalArgumentException();
526 long sz = size();
527 if (position > sz)
528 return 0;
529 int icount = (int)Math.min(count, Integer.MAX_VALUE);
530 if ((sz - position) < icount)
531 icount = (int)(sz - position);
532
533 long n;
534
535 // Attempt a direct transfer, if the kernel supports it
536 if ((n = transferToDirectly(position, icount, target)) >= 0)
537 return n;
538
539 // Attempt a mapped transfer, but only to trusted channel types
540 if ((n = transferToTrustedChannel(position, icount, target)) >= 0)
541 return n;
542
543 // Slow path for untrusted targets
544 return transferToArbitraryChannel(position, icount, target);
545 }
546
547 private long transferFromFileChannel(FileChannelImpl src,
548 long position, long count)
549 throws IOException
550 {
551 // Note we could loop here to accumulate more at once
552 synchronized (src.positionLock) {
553 long p = src.position();
554 int icount = (int)Math.min(Math.min(count, Integer.MAX_VALUE),
555 src.size() - p);
556 // ## Bug: Closing this channel will not terminate the write
557 MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, icount);
558 try {
559 long n = write(bb, position);
560 src.position(p + n);
561 return n;
562 } finally {
563 unmap(bb);
564 }
565 }
566 }
567
568 private static final int TRANSFER_SIZE = 8192;
569
570 private long transferFromArbitraryChannel(ReadableByteChannel src,
571 long position, long count)
572 throws IOException
573 {
574 // Untrusted target: Use a newly-erased buffer
575 int c = (int)Math.min(count, TRANSFER_SIZE);
576 ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
577 long tw = 0; // Total bytes written
578 long pos = position;
579 try {
580 Util.erase(bb);
581 while (tw < count) {
582 bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE));
583 // ## Bug: Will block reading src if this channel
584 // ## is asynchronously closed
585 int nr = src.read(bb);
586 if (nr <= 0)
587 break;
588 bb.flip();
589 int nw = write(bb, pos);
590 tw += nw;
591 if (nw != nr)
592 break;
593 pos += nw;
594 bb.clear();
595 }
596 return tw;
597 } catch (IOException x) {
598 if (tw > 0)
599 return tw;
600 throw x;
601 } finally {
602 Util.releaseTemporaryDirectBuffer(bb);
603 }
604 }
605
606 public long transferFrom(ReadableByteChannel src,
607 long position, long count)
608 throws IOException
609 {
610 ensureOpen();
611 if (!src.isOpen())
612 throw new ClosedChannelException();
613 if (!writable)
614 throw new NonWritableChannelException();
615 if ((position < 0) || (count < 0))
616 throw new IllegalArgumentException();
617 if (position > size())
618 return 0;
619 if (src instanceof FileChannelImpl)
620 return transferFromFileChannel((FileChannelImpl)src,
621 position, count);
622
623 return transferFromArbitraryChannel(src, position, count);
624 }
625
626 public int read(ByteBuffer dst, long position) throws IOException {
627 if (dst == null)
628 throw new NullPointerException();
629 if (position < 0)
630 throw new IllegalArgumentException("Negative position");
631 if (!readable)
632 throw new NonReadableChannelException();
633 ensureOpen();
634 int n = 0;
635 int ti = -1;
636 try {
637 begin();
638 if (!isOpen())
639 return -1;
640 ti = threads.add();
641 do {
642 n = IOUtil.read(fd, dst, position, nd, positionLock);
643 } while ((n == IOStatus.INTERRUPTED) && isOpen());
644 return IOStatus.normalize(n);
645 } finally {
646 threads.remove(ti);
647 end(n > 0);
648 assert IOStatus.check(n);
649 }
650 }
651
652 public int write(ByteBuffer src, long position) throws IOException {
653 if (src == null)
654 throw new NullPointerException();
655 if (position < 0)
656 throw new IllegalArgumentException("Negative position");
657 if (!writable)
658 throw new NonWritableChannelException();
659 ensureOpen();
660 int n = 0;
661 int ti = -1;
662 try {
663 begin();
664 if (!isOpen())
665 return -1;
666 ti = threads.add();
667 do {
668 n = IOUtil.write(fd, src, position, nd, positionLock);
669 } while ((n == IOStatus.INTERRUPTED) && isOpen());
670 return IOStatus.normalize(n);
671 } finally {
672 threads.remove(ti);
673 end(n > 0);
674 assert IOStatus.check(n);
675 }
676 }
677
678
679 // -- Memory-mapped buffers --
680
681 private static class Unmapper
682 implements Runnable
683 {
684
685 private long address;
686 private long size;
687
688 private Unmapper(long address, long size) {
689 assert (address != 0);
690 this.address = address;
691 this.size = size;
692 }
693
694 public void run() {
695 if (address == 0)
696 return;
697 unmap0(address, size);
698 address = 0;
699 }
700
701 }
702
703 private static void unmap(MappedByteBuffer bb) {
704 Cleaner cl = ((DirectBuffer)bb).cleaner();
705 if (cl != null)
706 cl.clean();
707 }
708
709 private static final int MAP_RO = 0;
710 private static final int MAP_RW = 1;
711 private static final int MAP_PV = 2;
712
713 public MappedByteBuffer map(MapMode mode, long position, long size)
714 throws IOException
715 {
716 ensureOpen();
717 if (position < 0L)
718 throw new IllegalArgumentException("Negative position");
719 if (size < 0L)
720 throw new IllegalArgumentException("Negative size");
721 if (position + size < 0)
722 throw new IllegalArgumentException("Position + size overflow");
723 if (size > Integer.MAX_VALUE)
724 throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
725 int imode = -1;
726 if (mode == MapMode.READ_ONLY)
727 imode = MAP_RO;
728 else if (mode == MapMode.READ_WRITE)
729 imode = MAP_RW;
730 else if (mode == MapMode.PRIVATE)
731 imode = MAP_PV;
732 assert (imode >= 0);
733 if ((mode != MapMode.READ_ONLY) && !writable)
734 throw new NonWritableChannelException();
735 if (!readable)
736 throw new NonReadableChannelException();
737
738 long addr = -1;
739 int ti = -1;
740 try {
741 begin();
742 if (!isOpen())
743 return null;
744 ti = threads.add();
745 if (size() < position + size) { // Extend file size
746 if (!writable) {
747 throw new IOException("Channel not open for writing " +
748 "- cannot extend file to required size");
749 }
750 int rv;
751 do {
752 rv = truncate0(fd, position + size);
753 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
754 }
755 if (size == 0) {
756 addr = 0;
757 if ((!writable) || (imode == MAP_RO))
758 return Util.newMappedByteBufferR(0, 0, null);
759 else
760 return Util.newMappedByteBuffer(0, 0, null);
761 }
762
763 int pagePosition = (int)(position % allocationGranularity);
764 long mapPosition = position - pagePosition;
765 long mapSize = size + pagePosition;
766 try {
767 // If no exception was thrown from map0, the address is valid
768 addr = map0(imode, mapPosition, mapSize);
769 } catch (OutOfMemoryError x) {
770 // An OutOfMemoryError may indicate that we've exhausted memory
771 // so force gc and re-attempt map
772 System.gc();
773 try {
774 Thread.sleep(100);
775 } catch (InterruptedException y) {
776 Thread.currentThread().interrupt();
777 }
778 try {
779 addr = map0(imode, mapPosition, mapSize);
780 } catch (OutOfMemoryError y) {
781 // After a second OOME, fail
782 throw new IOException("Map failed", y);
783 }
784 }
785
786 assert (IOStatus.checkAll(addr));
787 assert (addr % allocationGranularity == 0);
788 int isize = (int)size;
789 Unmapper um = new Unmapper(addr, size + pagePosition);
790 if ((!writable) || (imode == MAP_RO))
791 return Util.newMappedByteBufferR(isize, addr + pagePosition, um);
792 else
793 return Util.newMappedByteBuffer(isize, addr + pagePosition, um);
794 } finally {
795 threads.remove(ti);
796 end(IOStatus.checkAll(addr));
797 }
798 }
799
800
801 // -- Locks --
802
803 public static final int NO_LOCK = -1; // Failed to lock
804 public static final int LOCKED = 0; // Obtained requested lock
805 public static final int RET_EX_LOCK = 1; // Obtained exclusive lock
806 public static final int INTERRUPTED = 2; // Request interrupted
807
808 // keeps track of locks on this file
809 private volatile FileLockTable fileLockTable;
810
811 // indicates if file locks are maintained system-wide (as per spec)
812 private static boolean isSharedFileLockTable;
813
814 // indicates if the disableSystemWideOverlappingFileLockCheck property
815 // has been checked
816 private static volatile boolean propertyChecked;
817
818 // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so
819 // the overlap check wasn't system wide when there were multiple channels to
820 // the same file. This property is used to get 1.4/5.0 behavior if desired.
821 private static boolean isSharedFileLockTable() {
822 if (!propertyChecked) {
823 synchronized (FileChannelImpl.class) {
824 if (!propertyChecked) {
825 String value = AccessController.doPrivileged(
826 new GetPropertyAction(
827 "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"));
828 isSharedFileLockTable = ((value == null) || value.equals("false"));
829 propertyChecked = true;
830 }
831 }
832 }
833 return isSharedFileLockTable;
834 }
835
836 private FileLockTable fileLockTable() {
837 if (fileLockTable == null) {
838 synchronized (this) {
839 if (fileLockTable == null) {
840 fileLockTable = isSharedFileLockTable() ?
841 new SharedFileLockTable(this) : new SimpleFileLockTable();
842 }
843 }
844 }
845 return fileLockTable;
846 }
847
848 public FileLock lock(long position, long size, boolean shared)
849 throws IOException
850 {
851 ensureOpen();
852 if (shared && !readable)
853 throw new NonReadableChannelException();
854 if (!shared && !writable)
855 throw new NonWritableChannelException();
856 FileLockImpl fli = new FileLockImpl(this, position, size, shared);
857 FileLockTable flt = fileLockTable();
858 flt.add(fli);
859 boolean i = true;
860 int ti = -1;
861 try {
862 begin();
863 if (!isOpen())
864 return null;
865 ti = threads.add();
866 int result = lock0(fd, true, position, size, shared);
867 if (result == RET_EX_LOCK) {
868 assert shared;
869 FileLockImpl fli2 = new FileLockImpl(this, position, size,
870 false);
871 flt.replace(fli, fli2);
872 return fli2;
873 }
874 if (result == INTERRUPTED || result == NO_LOCK) {
875 flt.remove(fli);
876 i = false;
877 }
878 } catch (IOException e) {
879 flt.remove(fli);
880 throw e;
881 } finally {
882 threads.remove(ti);
883 try {
884 end(i);
885 } catch (ClosedByInterruptException e) {
886 throw new FileLockInterruptionException();
887 }
888 }
889 return fli;
890 }
891
892 public FileLock tryLock(long position, long size, boolean shared)
893 throws IOException
894 {
895 ensureOpen();
896 if (shared && !readable)
897 throw new NonReadableChannelException();
898 if (!shared && !writable)
899 throw new NonWritableChannelException();
900 FileLockImpl fli = new FileLockImpl(this, position, size, shared);
901 FileLockTable flt = fileLockTable();
902 flt.add(fli);
903 int result = lock0(fd, false, position, size, shared);
904 if (result == NO_LOCK) {
905 flt.remove(fli);
906 return null;
907 }
908 if (result == RET_EX_LOCK) {
909 assert shared;
910 FileLockImpl fli2 = new FileLockImpl(this, position, size,
911 false);
912 flt.replace(fli, fli2);
913 return fli2;
914 }
915 return fli;
916 }
917
918 void release(FileLockImpl fli) throws IOException {
919 ensureOpen();
920 release0(fd, fli.position(), fli.size());
921 assert fileLockTable != null;
922 fileLockTable.remove(fli);
923 }
924
925
926 // -- File lock support --
927
928 /**
929 * A table of FileLocks.
930 */
931 private interface FileLockTable {
932 /**
933 * Adds a file lock to the table.
934 *
935 * @throws OverlappingFileLockException if the file lock overlaps
936 * with an existing file lock in the table
937 */
938 void add(FileLock fl) throws OverlappingFileLockException;
939
940 /**
941 * Remove an existing file lock from the table.
942 */
943 void remove(FileLock fl);
944
945 /**
946 * An implementation of this interface releases a given file lock.
947 * Used with removeAll.
948 */
949 interface Releaser {
950 void release(FileLock fl) throws IOException;
951 }
952
953 /**
954 * Removes all file locks from the table.
955 * <p>
956 * The Releaser#release method is invoked for each file lock before
957 * it is removed.
958 *
959 * @throws IOException if the release method throws IOException
960 */
961 void removeAll(Releaser r) throws IOException;
962
963 /**
964 * Replaces an existing file lock in the table.
965 */
966 void replace(FileLock fl1, FileLock fl2);
967 }
968
969 /**
970 * A simple file lock table that maintains a list of FileLocks obtained by a
971 * FileChannel. Use to get 1.4/5.0 behaviour.
972 */
973 private static class SimpleFileLockTable implements FileLockTable {
974 // synchronize on list for access
975 private List<FileLock> lockList = new ArrayList<FileLock>(2);
976
977 public SimpleFileLockTable() {
978 }
979
980 private void checkList(long position, long size)
981 throws OverlappingFileLockException
982 {
983 assert Thread.holdsLock(lockList);
984 for (FileLock fl: lockList) {
985 if (fl.overlaps(position, size)) {
986 throw new OverlappingFileLockException();
987 }
988 }
989 }
990
991 public void add(FileLock fl) throws OverlappingFileLockException {
992 synchronized (lockList) {
993 checkList(fl.position(), fl.size());
994 lockList.add(fl);
995 }
996 }
997
998 public void remove(FileLock fl) {
999 synchronized (lockList) {
1000 lockList.remove(fl);
1001 }
1002 }
1003
1004 public void removeAll(Releaser releaser) throws IOException {
1005 synchronized(lockList) {
1006 Iterator<FileLock> i = lockList.iterator();
1007 while (i.hasNext()) {
1008 FileLock fl = i.next();
1009 releaser.release(fl);
1010 i.remove();
1011 }
1012 }
1013 }
1014
1015 public void replace(FileLock fl1, FileLock fl2) {
1016 synchronized (lockList) {
1017 lockList.remove(fl1);
1018 lockList.add(fl2);
1019 }
1020 }
1021 }
1022
1023 /**
1024 * A weak reference to a FileLock.
1025 * <p>
1026 * SharedFileLockTable uses a list of file lock references to avoid keeping the
1027 * FileLock (and FileChannel) alive.
1028 */
1029 private static class FileLockReference extends WeakReference<FileLock> {
1030 private FileKey fileKey;
1031
1032 FileLockReference(FileLock referent,
1033 ReferenceQueue<FileLock> queue,
1034 FileKey key) {
1035 super(referent, queue);
1036 this.fileKey = key;
1037 }
1038
1039 private FileKey fileKey() {
1040 return fileKey;
1041 }
1042 }
1043
1044 /**
1045 * A file lock table that is over a system-wide map of all file locks.
1046 */
1047 private static class SharedFileLockTable implements FileLockTable {
1048 // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
1049 // The map value is a list of file locks represented by FileLockReferences.
1050 // All access to the list must be synchronized on the list.
1051 private static ConcurrentHashMap<FileKey, ArrayList<FileLockReference>> lockMap =
1052 new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
1053
1054 // reference queue for cleared refs
1055 private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
1056
1057 // the enclosing file channel
1058 private FileChannelImpl fci;
1059
1060 // File key for the file that this channel is connected to
1061 private FileKey fileKey;
1062
1063 public SharedFileLockTable(FileChannelImpl fci) {
1064 this.fci = fci;
1065 this.fileKey = FileKey.create(fci.fd);
1066 }
1067
1068 public void add(FileLock fl) throws OverlappingFileLockException {
1069 ArrayList<FileLockReference> list = lockMap.get(fileKey);
1070
1071 for (;;) {
1072
1073 // The key isn't in the map so we try to create it atomically
1074 if (list == null) {
1075 list = new ArrayList<FileLockReference>(2);
1076 ArrayList<FileLockReference> prev;
1077 synchronized (list) {
1078 prev = lockMap.putIfAbsent(fileKey, list);
1079 if (prev == null) {
1080 // we successfully created the key so we add the file lock
1081 list.add(new FileLockReference(fl, queue, fileKey));
1082 break;
1083 }
1084 }
1085 // someone else got there first
1086 list = prev;
1087 }
1088
1089 // There is already a key. It is possible that some other thread
1090 // is removing it so we re-fetch the value from the map. If it
1091 // hasn't changed then we check the list for overlapping locks
1092 // and add the new lock to the list.
1093 synchronized (list) {
1094 ArrayList<FileLockReference> current = lockMap.get(fileKey);
1095 if (list == current) {
1096 checkList(list, fl.position(), fl.size());
1097 list.add(new FileLockReference(fl, queue, fileKey));
1098 break;
1099 }
1100 list = current;
1101 }
1102
1103 }
1104
1105 // process any stale entries pending in the reference queue
1106 removeStaleEntries();
1107 }
1108
1109 private void removeKeyIfEmpty(FileKey fk, ArrayList<FileLockReference> list) {
1110 assert Thread.holdsLock(list);
1111 assert lockMap.get(fk) == list;
1112 if (list.isEmpty()) {
1113 lockMap.remove(fk);
1114 }
1115 }
1116
1117 public void remove(FileLock fl) {
1118 assert fl != null;
1119
1120 // the lock must exist so the list of locks must be present
1121 ArrayList<FileLockReference> list = lockMap.get(fileKey);
1122 assert list != null;
1123
1124 synchronized (list) {
1125 int index = 0;
1126 while (index < list.size()) {
1127 FileLockReference ref = list.get(index);
1128 FileLock lock = ref.get();
1129 if (lock == fl) {
1130 assert (lock != null) && (lock.channel() == fci);
1131 ref.clear();
1132 list.remove(index);
1133 break;
1134 }
1135 index++;
1136 }
1137 }
1138 }
1139
1140 public void removeAll(Releaser releaser) throws IOException {
1141 ArrayList<FileLockReference> list = lockMap.get(fileKey);
1142 if (list != null) {
1143 synchronized (list) {
1144 int index = 0;
1145 while (index < list.size()) {
1146 FileLockReference ref = list.get(index);
1147 FileLock lock = ref.get();
1148
1149 // remove locks obtained by this channel
1150 if (lock != null && lock.channel() == fci) {
1151 // invoke the releaser to invalidate/release the lock
1152 releaser.release(lock);
1153
1154 // remove the lock from the list
1155 ref.clear();
1156 list.remove(index);
1157 } else {
1158 index++;
1159 }
1160 }
1161
1162 // once the lock list is empty we remove it from the map
1163 removeKeyIfEmpty(fileKey, list);
1164 }
1165 }
1166 }
1167
1168 public void replace(FileLock fromLock, FileLock toLock) {
1169 // the lock must exist so there must be a list
1170 ArrayList<FileLockReference> list = lockMap.get(fileKey);
1171 assert list != null;
1172
1173 synchronized (list) {
1174 for (int index=0; index<list.size(); index++) {
1175 FileLockReference ref = list.get(index);
1176 FileLock lock = ref.get();
1177 if (lock == fromLock) {
1178 ref.clear();
1179 list.set(index, new FileLockReference(toLock, queue, fileKey));
1180 break;
1181 }
1182 }
1183 }
1184 }
1185
1186 // Check for overlapping file locks
1187 private void checkList(List<FileLockReference> list, long position, long size)
1188 throws OverlappingFileLockException
1189 {
1190 assert Thread.holdsLock(list);
1191 for (FileLockReference ref: list) {
1192 FileLock fl = ref.get();
1193 if (fl != null && fl.overlaps(position, size))
1194 throw new OverlappingFileLockException();
1195 }
1196 }
1197
1198 // Process the reference queue
1199 private void removeStaleEntries() {
1200 FileLockReference ref;
1201 while ((ref = (FileLockReference)queue.poll()) != null) {
1202 FileKey fk = ref.fileKey();
1203 ArrayList<FileLockReference> list = lockMap.get(fk);
1204 if (list != null) {
1205 synchronized (list) {
1206 list.remove(ref);
1207 removeKeyIfEmpty(fk, list);
1208 }
1209 }
1210 }
1211 }
1212 }
1213
1214 // -- Native methods --
1215
1216 // Grabs a file lock
1217 native int lock0(FileDescriptor fd, boolean blocking, long pos, long size,
1218 boolean shared) throws IOException;
1219
1220 // Releases a file lock
1221 native void release0(FileDescriptor fd, long pos, long size)
1222 throws IOException;
1223
1224 // Creates a new mapping
1225 private native long map0(int prot, long position, long length)
1226 throws IOException;
1227
1228 // Removes an existing mapping
1229 private static native int unmap0(long address, long length);
1230
1231 // Forces output to device
1232 private native int force0(FileDescriptor fd, boolean metaData);
1233
1234 // Truncates a file
1235 private native int truncate0(FileDescriptor fd, long size);
1236
1237 // Transfers from src to dst, or returns -2 if kernel can't do that
1238 private native long transferTo0(int src, long position, long count, int dst);
1239
1240 // Sets or reports this file's position
1241 // If offset is -1, the current position is returned
1242 // otherwise the position is set to offset
1243 private native long position0(FileDescriptor fd, long offset);
1244
1245 // Reports this file's size
1246 private native long size0(FileDescriptor fd);
1247
1248 // Caches fieldIDs
1249 private static native long initIDs();
1250
1251 static {
1252 Util.load();
1253 allocationGranularity = initIDs();
1254 nd = new FileDispatcher();
1255 isAMappedBufferField = Reflect.lookupField("java.nio.MappedByteBuffer",
1256 "isAMappedBuffer");
1257 }
1258
1259 }