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