在用到FileChannel读文件的时候,里面有个read方法,参数分别为:
/**
* Reads a sequence of bytes from this channel into a subsequence of the
* given buffers.
*
* <p> Bytes are read starting at this channel's current file position, and
* then the file position is updated with the number of bytes actually
* read. Otherwise this method behaves exactly as specified in the {@link
* ScatteringByteChannel} interface. </p>
*/
public abstract long read(ByteBuffer[] dsts, int offset, int length)
throws IOException;
这个方法也很正常,和Io流中的read方法也差不多,只不过是把byte数组换成了ByteBuffer数组。考虑到ByteBuffer的开销很大,每次开的内存也不是byte可以比的,所以我打算省着点儿用,由于文件只有48KB,所以我就开了一个1024的ByteBuffer:
ByteBuffer[] dsts = new ByteBuffer[1];
dsts[0]=ByteBuffer.allocate(1024);
/**File:48,563Byte*/
@SuppressWarnings("resource")
FileChannel fc = new FileInputStream(new File(PATH)).getChannel();
/**offset>=0 length>0 dsts.length-length=1-1024<offset*/
fc.read(dsts, 0, 1024);
fc.close();
结果一运行,报错了:
Exception in thread "main" java.lang.IndexOutOfBoundsException
at sun.nio.ch.FileChannelImpl.read(Unknown Source)
at com.a2.desktop.example8.bytebuff.TestByteBuffer.main(TestByteBuffer.java:23)
找到FileChannelImpl.java文件,找到这个方法:
154 public long read(ByteBuffer[] dsts, int offset, int length)
155 throws IOException
156 {
157 if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
158 throw new IndexOutOfBoundsException();
159 ensureOpen();
160 if (!readable)
161 throw new NonReadableChannelException();
162 synchronized (positionLock) {
163 long n = 0;
164 int ti = -1;
165 try {
166 begin();
167 ti = threads.add();
168 if (!isOpen())
169 return 0;
170 do {
171 n = IOUtil.read(fd, dsts, offset, length, nd);
172 } while ((n == IOStatus.INTERRUPTED) && isOpen());
173 return IOStatus.normalize(n);
174 } finally {
175 threads.remove(ti);
176 end(n > 0);
177 assert IOStatus.check(n);
178 }
179 }
180 }
原来还需要满足offset > dsts.length – length。最近比较忙,没有时间去看底层的实现,但是这样做也合乎情理,最后附上sun实现后的FileChannel:
1 /*
2 * Copyright (c) 2000, 2011, 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.io.FileDescriptor;
29 import java.io.IOException;
30 import java.nio.ByteBuffer;
31 import java.nio.MappedByteBuffer;
32 import java.nio.channels;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.security.AccessController;
36 import sun.misc.Cleaner;
37 import sun.security.action.GetPropertyAction;
38
39 public class FileChannelImpl
40 extends FileChannel
41 {
42 // Memory allocation size for mapping buffers
43 private static final long allocationGranularity;
44
45 // Used to make native read and write calls
46 private final FileDispatcher nd;
47
48 // File descriptor
49 private final FileDescriptor fd;
50
51 // File access mode (immutable)
52 private final boolean writable;
53 private final boolean readable;
54 private final boolean append;
55
56 // Required to prevent finalization of creating stream (immutable)
57 private final Object parent;
58
59 // Thread-safe set of IDs of native threads, for signalling
60 private final NativeThreadSet threads = new NativeThreadSet(2);
61
62 // Lock for operations involving position and size
63 private final Object positionLock = new Object();
64
65 private FileChannelImpl(FileDescriptor fd, boolean readable,
66 boolean writable, boolean append, Object parent)
67 {
68 this.fd = fd;
69 this.readable = readable;
70 this.writable = writable;
71 this.append = append;
72 this.parent = parent;
73 this.nd = new FileDispatcherImpl(append);
74 }
75
76 // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
77 public static FileChannel open(FileDescriptor fd,
78 boolean readable, boolean writable,
79 Object parent)
80 {
81 return new FileChannelImpl(fd, readable, writable, false, parent);
82 }
83
84 // Used by FileOutputStream.getChannel
85 public static FileChannel open(FileDescriptor fd,
86 boolean readable, boolean writable,
87 boolean append, Object parent)
88 {
89 return new FileChannelImpl(fd, readable, writable, append, parent);
90 }
91
92 private void ensureOpen() throws IOException {
93 if (!isOpen())
94 throw new ClosedChannelException();
95 }
96
97
98 // -- Standard channel operations --
99
100 protected void implCloseChannel() throws IOException {
101 // Release and invalidate any locks that we still hold
102 if (fileLockTable != null) {
103 for (FileLock fl: fileLockTable.removeAll()) {
104 synchronized (fl) {
105 if (fl.isValid()) {
106 nd.release(fd, fl.position(), fl.size());
107 ((FileLockImpl)fl).invalidate();
108 }
109 }
110 }
111 }
112
113 nd.preClose(fd);
114 threads.signalAndWait();
115
116 if (parent != null) {
117
118 // Close the fd via the parent stream's close method. The parent
119 // will reinvoke our close method, which is defined in the
120 // superclass AbstractInterruptibleChannel, but the isOpen logic in
121 // that method will prevent this method from being reinvoked.
122 //
123 ((java.io.Closeable)parent).close();
124 } else {
125 nd.close(fd);
126 }
127
128 }
129
130 public int read(ByteBuffer dst) throws IOException {
131 ensureOpen();
132 if (!readable)
133 throw new NonReadableChannelException();
134 synchronized (positionLock) {
135 int n = 0;
136 int ti = -1;
137 try {
138 begin();
139 ti = threads.add();
140 if (!isOpen())
141 return 0;
142 do {
143 n = IOUtil.read(fd, dst, -1, nd, positionLock);
144 } while ((n == IOStatus.INTERRUPTED) && isOpen());
145 return IOStatus.normalize(n);
146 } finally {
147 threads.remove(ti);
148 end(n > 0);
149 assert IOStatus.check(n);
150 }
151 }
152 }
153
154 public long read(ByteBuffer[] dsts, int offset, int length)
155 throws IOException
156 {
157 if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
158 throw new IndexOutOfBoundsException();
159 ensureOpen();
160 if (!readable)
161 throw new NonReadableChannelException();
162 synchronized (positionLock) {
163 long n = 0;
164 int ti = -1;
165 try {
166 begin();
167 ti = threads.add();
168 if (!isOpen())
169 return 0;
170 do {
171 n = IOUtil.read(fd, dsts, offset, length, nd);
172 } while ((n == IOStatus.INTERRUPTED) && isOpen());
173 return IOStatus.normalize(n);
174 } finally {
175 threads.remove(ti);
176 end(n > 0);
177 assert IOStatus.check(n);
178 }
179 }
180 }
181
182 public int write(ByteBuffer src) throws IOException {
183 ensureOpen();
184 if (!writable)
185 throw new NonWritableChannelException();
186 synchronized (positionLock) {
187 int n = 0;
188 int ti = -1;
189 try {
190 begin();
191 ti = threads.add();
192 if (!isOpen())
193 return 0;
194 do {
195 n = IOUtil.write(fd, src, -1, nd, positionLock);
196 } while ((n == IOStatus.INTERRUPTED) && isOpen());
197 return IOStatus.normalize(n);
198 } finally {
199 threads.remove(ti);
200 end(n > 0);
201 assert IOStatus.check(n);
202 }
203 }
204 }
205
206 public long write(ByteBuffer[] srcs, int offset, int length)
207 throws IOException
208 {
209 if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
210 throw new IndexOutOfBoundsException();
211 ensureOpen();
212 if (!writable)
213 throw new NonWritableChannelException();
214 synchronized (positionLock) {
215 long n = 0;
216 int ti = -1;
217 try {
218 begin();
219 ti = threads.add();
220 if (!isOpen())
221 return 0;
222 do {
223 n = IOUtil.write(fd, srcs, offset, length, nd);
224 } while ((n == IOStatus.INTERRUPTED) && isOpen());
225 return IOStatus.normalize(n);
226 } finally {
227 threads.remove(ti);
228 end(n > 0);
229 assert IOStatus.check(n);
230 }
231 }
232 }
233
234 // -- Other operations --
235
236 public long position() throws IOException {
237 ensureOpen();
238 synchronized (positionLock) {
239 long p = -1;
240 int ti = -1;
241 try {
242 begin();
243 ti = threads.add();
244 if (!isOpen())
245 return 0;
246 do {
247 // in append-mode then position is advanced to end before writing
248 p = (append) ? nd.size(fd) : position0(fd, -1);
249 } while ((p == IOStatus.INTERRUPTED) && isOpen());
250 return IOStatus.normalize(p);
251 } finally {
252 threads.remove(ti);
253 end(p > -1);
254 assert IOStatus.check(p);
255 }
256 }
257 }
258
259 public FileChannel position(long newPosition) throws IOException {
260 ensureOpen();
261 if (newPosition < 0)
262 throw new IllegalArgumentException();
263 synchronized (positionLock) {
264 long p = -1;
265 int ti = -1;
266 try {
267 begin();
268 ti = threads.add();
269 if (!isOpen())
270 return null;
271 do {
272 p = position0(fd, newPosition);
273 } while ((p == IOStatus.INTERRUPTED) && isOpen());
274 return this;
275 } finally {
276 threads.remove(ti);
277 end(p > -1);
278 assert IOStatus.check(p);
279 }
280 }
281 }
282
283 public long size() throws IOException {
284 ensureOpen();
285 synchronized (positionLock) {
286 long s = -1;
287 int ti = -1;
288 try {
289 begin();
290 ti = threads.add();
291 if (!isOpen())
292 return -1;
293 do {
294 s = nd.size(fd);
295 } while ((s == IOStatus.INTERRUPTED) && isOpen());
296 return IOStatus.normalize(s);
297 } finally {
298 threads.remove(ti);
299 end(s > -1);
300 assert IOStatus.check(s);
301 }
302 }
303 }
304
305 public FileChannel truncate(long size) throws IOException {
306 ensureOpen();
307 if (size < 0)
308 throw new IllegalArgumentException();
309 if (size > size())
310 return this;
311 if (!writable)
312 throw new NonWritableChannelException();
313 synchronized (positionLock) {
314 int rv = -1;
315 long p = -1;
316 int ti = -1;
317 try {
318 begin();
319 ti = threads.add();
320 if (!isOpen())
321 return null;
322
323 // get current position
324 do {
325 p = position0(fd, -1);
326 } while ((p == IOStatus.INTERRUPTED) && isOpen());
327 if (!isOpen())
328 return null;
329 assert p >= 0;
330
331 // truncate file
332 do {
333 rv = nd.truncate(fd, size);
334 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
335 if (!isOpen())
336 return null;
337
338 // set position to size if greater than size
339 if (p > size)
340 p = size;
341 do {
342 rv = (int)position0(fd, p);
343 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
344 return this;
345 } finally {
346 threads.remove(ti);
347 end(rv > -1);
348 assert IOStatus.check(rv);
349 }
350 }
351 }
352
353 public void force(boolean metaData) throws IOException {
354 ensureOpen();
355 int rv = -1;
356 int ti = -1;
357 try {
358 begin();
359 ti = threads.add();
360 if (!isOpen())
361 return;
362 do {
363 rv = nd.force(fd, metaData);
364 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
365 } finally {
366 threads.remove(ti);
367 end(rv > -1);
368 assert IOStatus.check(rv);
369 }
370 }
371
372 // Assume at first that the underlying kernel supports sendfile();
373 // set this to false if we find out later that it doesn't
374 //
375 private static volatile boolean transferSupported = true;
376
377 // Assume that the underlying kernel sendfile() will work if the target
378 // fd is a pipe; set this to false if we find out later that it doesn't
379 //
380 private static volatile boolean pipeSupported = true;
381
382 // Assume that the underlying kernel sendfile() will work if the target
383 // fd is a file; set this to false if we find out later that it doesn't
384 //
385 private static volatile boolean fileSupported = true;
386
387 private long transferToDirectly(long position, int icount,
388 WritableByteChannel target)
389 throws IOException
390 {
391 if (!transferSupported)
392 return IOStatus.UNSUPPORTED;
393
394 FileDescriptor targetFD = null;
395 if (target instanceof FileChannelImpl) {
396 if (!fileSupported)
397 return IOStatus.UNSUPPORTED_CASE;
398 targetFD = ((FileChannelImpl)target).fd;
399 } else if (target instanceof SelChImpl) {
400 // Direct transfer to pipe causes EINVAL on some configurations
401 if ((target instanceof SinkChannelImpl) && !pipeSupported)
402 return IOStatus.UNSUPPORTED_CASE;
403 targetFD = ((SelChImpl)target).getFD();
404 }
405 if (targetFD == null)
406 return IOStatus.UNSUPPORTED;
407 int thisFDVal = IOUtil.fdVal(fd);
408 int targetFDVal = IOUtil.fdVal(targetFD);
409 if (thisFDVal == targetFDVal) // Not supported on some configurations
410 return IOStatus.UNSUPPORTED;
411
412 long n = -1;
413 int ti = -1;
414 try {
415 begin();
416 ti = threads.add();
417 if (!isOpen())
418 return -1;
419 do {
420 n = transferTo0(thisFDVal, position, icount, targetFDVal);
421 } while ((n == IOStatus.INTERRUPTED) && isOpen());
422 if (n == IOStatus.UNSUPPORTED_CASE) {
423 if (target instanceof SinkChannelImpl)
424 pipeSupported = false;
425 if (target instanceof FileChannelImpl)
426 fileSupported = false;
427 return IOStatus.UNSUPPORTED_CASE;
428 }
429 if (n == IOStatus.UNSUPPORTED) {
430 // Don't bother trying again
431 transferSupported = false;
432 return IOStatus.UNSUPPORTED;
433 }
434 return IOStatus.normalize(n);
435 } finally {
436 threads.remove(ti);
437 end (n > -1);
438 }
439 }
440
441 // Maximum size to map when using a mapped buffer
442 private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L;
443
444 private long transferToTrustedChannel(long position, long count,
445 WritableByteChannel target)
446 throws IOException
447 {
448 boolean isSelChImpl = (target instanceof SelChImpl);
449 if (!((target instanceof FileChannelImpl) || isSelChImpl))
450 return IOStatus.UNSUPPORTED;
451
452 // Trusted target: Use a mapped buffer
453 long remaining = count;
454 while (remaining > 0L) {
455 long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);
456 try {
457 MappedByteBuffer dbb = map(MapMode.READ_ONLY, position, size);
458 try {
459 // ## Bug: Closing this channel will not terminate the write
460 int n = target.write(dbb);
461 assert n >= 0;
462 remaining -= n;
463 if (isSelChImpl) {
464 // one attempt to write to selectable channel
465 break;
466 }
467 assert n > 0;
468 position += n;
469 } finally {
470 unmap(dbb);
471 }
472 } catch (ClosedByInterruptException e) {
473 // target closed by interrupt as ClosedByInterruptException needs
474 // to be thrown after closing this channel.
475 assert !target.isOpen();
476 try {
477 close();
478 } catch (Throwable suppressed) {
479 e.addSuppressed(suppressed);
480 }
481 throw e;
482 } catch (IOException ioe) {
483 // Only throw exception if no bytes have been written
484 if (remaining == count)
485 throw ioe;
486 break;
487 }
488 }
489 return count - remaining;
490 }
491
492 private long transferToArbitraryChannel(long position, int icount,
493 WritableByteChannel target)
494 throws IOException
495 {
496 // Untrusted target: Use a newly-erased buffer
497 int c = Math.min(icount, TRANSFER_SIZE);
498 ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
499 long tw = 0; // Total bytes written
500 long pos = position;
501 try {
502 Util.erase(bb);
503 while (tw < icount) {
504 bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE));
505 int nr = read(bb, pos);
506 if (nr <= 0)
507 break;
508 bb.flip();
509 // ## Bug: Will block writing target if this channel
510 // ## is asynchronously closed
511 int nw = target.write(bb);
512 tw += nw;
513 if (nw != nr)
514 break;
515 pos += nw;
516 bb.clear();
517 }
518 return tw;
519 } catch (IOException x) {
520 if (tw > 0)
521 return tw;
522 throw x;
523 } finally {
524 Util.releaseTemporaryDirectBuffer(bb);
525 }
526 }
527
528 public long transferTo(long position, long count,
529 WritableByteChannel target)
530 throws IOException
531 {
532 ensureOpen();
533 if (!target.isOpen())
534 throw new ClosedChannelException();
535 if (!readable)
536 throw new NonReadableChannelException();
537 if (target instanceof FileChannelImpl &&
538 !((FileChannelImpl)target).writable)
539 throw new NonWritableChannelException();
540 if ((position < 0) || (count < 0))
541 throw new IllegalArgumentException();
542 long sz = size();
543 if (position > sz)
544 return 0;
545 int icount = (int)Math.min(count, Integer.MAX_VALUE);
546 if ((sz - position) < icount)
547 icount = (int)(sz - position);
548
549 long n;
550
551 // Attempt a direct transfer, if the kernel supports it
552 if ((n = transferToDirectly(position, icount, target)) >= 0)
553 return n;
554
555 // Attempt a mapped transfer, but only to trusted channel types
556 if ((n = transferToTrustedChannel(position, icount, target)) >= 0)
557 return n;
558
559 // Slow path for untrusted targets
560 return transferToArbitraryChannel(position, icount, target);
561 }
562
563 private long transferFromFileChannel(FileChannelImpl src,
564 long position, long count)
565 throws IOException
566 {
567 if (!src.readable)
568 throw new NonReadableChannelException();
569 synchronized (src.positionLock) {
570 long pos = src.position();
571 long max = Math.min(count, src.size() - pos);
572
573 long remaining = max;
574 long p = pos;
575 while (remaining > 0L) {
576 long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);
577 // ## Bug: Closing this channel will not terminate the write
578 MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, size);
579 try {
580 long n = write(bb, position);
581 assert n > 0;
582 p += n;
583 position += n;
584 remaining -= n;
585 } catch (IOException ioe) {
586 // Only throw exception if no bytes have been written
587 if (remaining == max)
588 throw ioe;
589 break;
590 } finally {
591 unmap(bb);
592 }
593 }
594 long nwritten = max - remaining;
595 src.position(pos + nwritten);
596 return nwritten;
597 }
598 }
599
600 private static final int TRANSFER_SIZE = 8192;
601
602 private long transferFromArbitraryChannel(ReadableByteChannel src,
603 long position, long count)
604 throws IOException
605 {
606 // Untrusted target: Use a newly-erased buffer
607 int c = (int)Math.min(count, TRANSFER_SIZE);
608 ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
609 long tw = 0; // Total bytes written
610 long pos = position;
611 try {
612 Util.erase(bb);
613 while (tw < count) {
614 bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE));
615 // ## Bug: Will block reading src if this channel
616 // ## is asynchronously closed
617 int nr = src.read(bb);
618 if (nr <= 0)
619 break;
620 bb.flip();
621 int nw = write(bb, pos);
622 tw += nw;
623 if (nw != nr)
624 break;
625 pos += nw;
626 bb.clear();
627 }
628 return tw;
629 } catch (IOException x) {
630 if (tw > 0)
631 return tw;
632 throw x;
633 } finally {
634 Util.releaseTemporaryDirectBuffer(bb);
635 }
636 }
637
638 public long transferFrom(ReadableByteChannel src,
639 long position, long count)
640 throws IOException
641 {
642 ensureOpen();
643 if (!src.isOpen())
644 throw new ClosedChannelException();
645 if (!writable)
646 throw new NonWritableChannelException();
647 if ((position < 0) || (count < 0))
648 throw new IllegalArgumentException();
649 if (position > size())
650 return 0;
651 if (src instanceof FileChannelImpl)
652 return transferFromFileChannel((FileChannelImpl)src,
653 position, count);
654
655 return transferFromArbitraryChannel(src, position, count);
656 }
657
658 public int read(ByteBuffer dst, long position) throws IOException {
659 if (dst == null)
660 throw new NullPointerException();
661 if (position < 0)
662 throw new IllegalArgumentException("Negative position");
663 if (!readable)
664 throw new NonReadableChannelException();
665 ensureOpen();
666 int n = 0;
667 int ti = -1;
668 try {
669 begin();
670 ti = threads.add();
671 if (!isOpen())
672 return -1;
673 do {
674 n = IOUtil.read(fd, dst, position, nd, positionLock);
675 } while ((n == IOStatus.INTERRUPTED) && isOpen());
676 return IOStatus.normalize(n);
677 } finally {
678 threads.remove(ti);
679 end(n > 0);
680 assert IOStatus.check(n);
681 }
682 }
683
684 public int write(ByteBuffer src, long position) throws IOException {
685 if (src == null)
686 throw new NullPointerException();
687 if (position < 0)
688 throw new IllegalArgumentException("Negative position");
689 if (!writable)
690 throw new NonWritableChannelException();
691 ensureOpen();
692 int n = 0;
693 int ti = -1;
694 try {
695 begin();
696 ti = threads.add();
697 if (!isOpen())
698 return -1;
699 do {
700 n = IOUtil.write(fd, src, position, nd, positionLock);
701 } while ((n == IOStatus.INTERRUPTED) && isOpen());
702 return IOStatus.normalize(n);
703 } finally {
704 threads.remove(ti);
705 end(n > 0);
706 assert IOStatus.check(n);
707 }
708 }
709
710
711 // -- Memory-mapped buffers --
712
713 private static class Unmapper
714 implements Runnable
715 {
716 // may be required to close file
717 private static final NativeDispatcher nd = new FileDispatcherImpl();
718
719 // keep track of mapped buffer usage
720 static volatile int count;
721 static volatile long totalSize;
722 static volatile long totalCapacity;
723
724 private volatile long address;
725 private final long size;
726 private final int cap;
727 private final FileDescriptor fd;
728
729 private Unmapper(long address, long size, int cap,
730 FileDescriptor fd)
731 {
732 assert (address != 0);
733 this.address = address;
734 this.size = size;
735 this.cap = cap;
736 this.fd = fd;
737
738 synchronized (Unmapper.class) {
739 count++;
740 totalSize += size;
741 totalCapacity += cap;
742 }
743 }
744
745 public void run() {
746 if (address == 0)
747 return;
748 unmap0(address, size);
749 address = 0;
750
751 // if this mapping has a valid file descriptor then we close it
752 if (fd.valid()) {
753 try {
754 nd.close(fd);
755 } catch (IOException ignore) {
756 // nothing we can do
757 }
758 }
759
760 synchronized (Unmapper.class) {
761 count--;
762 totalSize -= size;
763 totalCapacity -= cap;
764 }
765 }
766 }
767
768 private static void unmap(MappedByteBuffer bb) {
769 Cleaner cl = ((DirectBuffer)bb).cleaner();
770 if (cl != null)
771 cl.clean();
772 }
773
774 private static final int MAP_RO = 0;
775 private static final int MAP_RW = 1;
776 private static final int MAP_PV = 2;
777
778 public MappedByteBuffer map(MapMode mode, long position, long size)
779 throws IOException
780 {
781 ensureOpen();
782 if (position < 0L)
783 throw new IllegalArgumentException("Negative position");
784 if (size < 0L)
785 throw new IllegalArgumentException("Negative size");
786 if (position + size < 0)
787 throw new IllegalArgumentException("Position + size overflow");
788 if (size > Integer.MAX_VALUE)
789 throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
790 int imode = -1;
791 if (mode == MapMode.READ_ONLY)
792 imode = MAP_RO;
793 else if (mode == MapMode.READ_WRITE)
794 imode = MAP_RW;
795 else if (mode == MapMode.PRIVATE)
796 imode = MAP_PV;
797 assert (imode >= 0);
798 if ((mode != MapMode.READ_ONLY) && !writable)
799 throw new NonWritableChannelException();
800 if (!readable)
801 throw new NonReadableChannelException();
802
803 long addr = -1;
804 int ti = -1;
805 try {
806 begin();
807 ti = threads.add();
808 if (!isOpen())
809 return null;
810 if (size() < position + size) { // Extend file size
811 if (!writable) {
812 throw new IOException("Channel not open for writing " +
813 "- cannot extend file to required size");
814 }
815 int rv;
816 do {
817 rv = nd.truncate(fd, position + size);
818 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
819 }
820 if (size == 0) {
821 addr = 0;
822 // a valid file descriptor is not required
823 FileDescriptor dummy = new FileDescriptor();
824 if ((!writable) || (imode == MAP_RO))
825 return Util.newMappedByteBufferR(0, 0, dummy, null);
826 else
827 return Util.newMappedByteBuffer(0, 0, dummy, null);
828 }
829
830 int pagePosition = (int)(position % allocationGranularity);
831 long mapPosition = position - pagePosition;
832 long mapSize = size + pagePosition;
833 try {
834 // If no exception was thrown from map0, the address is valid
835 addr = map0(imode, mapPosition, mapSize);
836 } catch (OutOfMemoryError x) {
837 // An OutOfMemoryError may indicate that we've exhausted memory
838 // so force gc and re-attempt map
839 System.gc();
840 try {
841 Thread.sleep(100);
842 } catch (InterruptedException y) {
843 Thread.currentThread().interrupt();
844 }
845 try {
846 addr = map0(imode, mapPosition, mapSize);
847 } catch (OutOfMemoryError y) {
848 // After a second OOME, fail
849 throw new IOException("Map failed", y);
850 }
851 }
852
853 // On Windows, and potentially other platforms, we need an open
854 // file descriptor for some mapping operations.
855 FileDescriptor mfd;
856 try {
857 mfd = nd.duplicateForMapping(fd);
858 } catch (IOException ioe) {
859 unmap0(addr, mapSize);
860 throw ioe;
861 }
862
863 assert (IOStatus.checkAll(addr));
864 assert (addr % allocationGranularity == 0);
865 int isize = (int)size;
866 Unmapper um = new Unmapper(addr, mapSize, isize, mfd);
867 if ((!writable) || (imode == MAP_RO)) {
868 return Util.newMappedByteBufferR(isize,
869 addr + pagePosition,
870 mfd,
871 um);
872 } else {
873 return Util.newMappedByteBuffer(isize,
874 addr + pagePosition,
875 mfd,
876 um);
877 }
878 } finally {
879 threads.remove(ti);
880 end(IOStatus.checkAll(addr));
881 }
882 }
883
884 /**
885 * Invoked by sun.management.ManagementFactoryHelper to create the management
886 * interface for mapped buffers.
887 */
888 public static sun.misc.JavaNioAccess.BufferPool getMappedBufferPool() {
889 return new sun.misc.JavaNioAccess.BufferPool() {
890 @Override
891 public String getName() {
892 return "mapped";
893 }
894 @Override
895 public long getCount() {
896 return Unmapper.count;
897 }
898 @Override
899 public long getTotalCapacity() {
900 return Unmapper.totalCapacity;
901 }
902 @Override
903 public long getMemoryUsed() {
904 return Unmapper.totalSize;
905 }
906 };
907 }
908
909 // -- Locks --
910
911
912
913 // keeps track of locks on this file
914 private volatile FileLockTable fileLockTable;
915
916 // indicates if file locks are maintained system-wide (as per spec)
917 private static boolean isSharedFileLockTable;
918
919 // indicates if the disableSystemWideOverlappingFileLockCheck property
920 // has been checked
921 private static volatile boolean propertyChecked;
922
923 // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so
924 // the overlap check wasn't system wide when there were multiple channels to
925 // the same file. This property is used to get 1.4/5.0 behavior if desired.
926 private static boolean isSharedFileLockTable() {
927 if (!propertyChecked) {
928 synchronized (FileChannelImpl.class) {
929 if (!propertyChecked) {
930 String value = AccessController.doPrivileged(
931 new GetPropertyAction(
932 "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"));
933 isSharedFileLockTable = ((value == null) || value.equals("false"));
934 propertyChecked = true;
935 }
936 }
937 }
938 return isSharedFileLockTable;
939 }
940
941 private FileLockTable fileLockTable() throws IOException {
942 if (fileLockTable == null) {
943 synchronized (this) {
944 if (fileLockTable == null) {
945 if (isSharedFileLockTable()) {
946 int ti = threads.add();
947 try {
948 ensureOpen();
949 fileLockTable = FileLockTable.newSharedFileLockTable(this, fd);
950 } finally {
951 threads.remove(ti);
952 }
953 } else {
954 fileLockTable = new SimpleFileLockTable();
955 }
956 }
957 }
958 }
959 return fileLockTable;
960 }
961
962 public FileLock lock(long position, long size, boolean shared)
963 throws IOException
964 {
965 ensureOpen();
966 if (shared && !readable)
967 throw new NonReadableChannelException();
968 if (!shared && !writable)
969 throw new NonWritableChannelException();
970 FileLockImpl fli = new FileLockImpl(this, position, size, shared);
971 FileLockTable flt = fileLockTable();
972 flt.add(fli);
973 boolean completed = false;
974 int ti = -1;
975 try {
976 begin();
977 ti = threads.add();
978 if (!isOpen())
979 return null;
980 int n;
981 do {
982 n = nd.lock(fd, true, position, size, shared);
983 } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
984 if (isOpen()) {
985 if (n == FileDispatcher.RET_EX_LOCK) {
986 assert shared;
987 FileLockImpl fli2 = new FileLockImpl(this, position, size,
988 false);
989 flt.replace(fli, fli2);
990 fli = fli2;
991 }
992 completed = true;
993 }
994 } finally {
995 if (!completed)
996 flt.remove(fli);
997 threads.remove(ti);
998 try {
999 end(completed);
1000 } catch (ClosedByInterruptException e) {
1001 throw new FileLockInterruptionException();
1002 }
1003 }
1004 return fli;
1005 }
1006
1007 public FileLock tryLock(long position, long size, boolean shared)
1008 throws IOException
1009 {
1010 ensureOpen();
1011 if (shared && !readable)
1012 throw new NonReadableChannelException();
1013 if (!shared && !writable)
1014 throw new NonWritableChannelException();
1015 FileLockImpl fli = new FileLockImpl(this, position, size, shared);
1016 FileLockTable flt = fileLockTable();
1017 flt.add(fli);
1018 int result;
1019
1020 int ti = threads.add();
1021 try {
1022 try {
1023 ensureOpen();
1024 result = nd.lock(fd, false, position, size, shared);
1025 } catch (IOException e) {
1026 flt.remove(fli);
1027 throw e;
1028 }
1029 if (result == FileDispatcher.NO_LOCK) {
1030 flt.remove(fli);
1031 return null;
1032 }
1033 if (result == FileDispatcher.RET_EX_LOCK) {
1034 assert shared;
1035 FileLockImpl fli2 = new FileLockImpl(this, position, size,
1036 false);
1037 flt.replace(fli, fli2);
1038 return fli2;
1039 }
1040 return fli;
1041 } finally {
1042 threads.remove(ti);
1043 }
1044 }
1045
1046 void release(FileLockImpl fli) throws IOException {
1047 int ti = threads.add();
1048 try {
1049 ensureOpen();
1050 nd.release(fd, fli.position(), fli.size());
1051 } finally {
1052 threads.remove(ti);
1053 }
1054 assert fileLockTable != null;
1055 fileLockTable.remove(fli);
1056 }
1057
1058 // -- File lock support --
1059
1060 /**
1061 * A simple file lock table that maintains a list of FileLocks obtained by a
1062 * FileChannel. Use to get 1.4/5.0 behaviour.
1063 */
1064 private static class SimpleFileLockTable extends FileLockTable {
1065 // synchronize on list for access
1066 private final List<FileLock> lockList = new ArrayList<FileLock>(2);
1067
1068 public SimpleFileLockTable() {
1069 }
1070
1071 private void checkList(long position, long size)
1072 throws OverlappingFileLockException
1073 {
1074 assert Thread.holdsLock(lockList);
1075 for (FileLock fl: lockList) {
1076 if (fl.overlaps(position, size)) {
1077 throw new OverlappingFileLockException();
1078 }
1079 }
1080 }
1081
1082 public void add(FileLock fl) throws OverlappingFileLockException {
1083 synchronized (lockList) {
1084 checkList(fl.position(), fl.size());
1085 lockList.add(fl);
1086 }
1087 }
1088
1089 public void remove(FileLock fl) {
1090 synchronized (lockList) {
1091 lockList.remove(fl);
1092 }
1093 }
1094
1095 public List<FileLock> removeAll() {
1096 synchronized(lockList) {
1097 List<FileLock> result = new ArrayList<FileLock>(lockList);
1098 lockList.clear();
1099 return result;
1100 }
1101 }
1102
1103 public void replace(FileLock fl1, FileLock fl2) {
1104 synchronized (lockList) {
1105 lockList.remove(fl1);
1106 lockList.add(fl2);
1107 }
1108 }
1109 }
1110
1111 // -- Native methods --
1112
1113 // Creates a new mapping
1114 private native long map0(int prot, long position, long length)
1115 throws IOException;
1116
1117 // Removes an existing mapping
1118 private static native int unmap0(long address, long length);
1119
1120 // Transfers from src to dst, or returns -2 if kernel can't do that
1121 private native long transferTo0(int src, long position, long count, int dst);
1122
1123 // Sets or reports this file's position
1124 // If offset is -1, the current position is returned
1125 // otherwise the position is set to offset
1126 private native long position0(FileDescriptor fd, long offset);
1127
1128 // Caches fieldIDs
1129 private static native long initIDs();
1130
1131 static {
1132 Util.load();
1133 allocationGranularity = initIDs();
1134 }
1135
1136 }