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