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 }