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