1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.jimfs; 18 19 import static com.google.common.jimfs.TestUtils.assertNotEquals; 20 import static com.google.common.jimfs.TestUtils.buffer; 21 import static com.google.common.jimfs.TestUtils.bytes; 22 import static com.google.common.jimfs.TestUtils.regularFile; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 import static java.nio.file.StandardOpenOption.APPEND; 25 import static java.nio.file.StandardOpenOption.READ; 26 import static java.nio.file.StandardOpenOption.WRITE; 27 import static java.util.concurrent.TimeUnit.MILLISECONDS; 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertNotNull; 31 import static org.junit.Assert.assertSame; 32 import static org.junit.Assert.assertTrue; 33 import static org.junit.Assert.fail; 34 35 import com.google.common.collect.ImmutableSet; 36 import com.google.common.testing.NullPointerTester; 37 import com.google.common.util.concurrent.Runnables; 38 import com.google.common.util.concurrent.SettableFuture; 39 import com.google.common.util.concurrent.Uninterruptibles; 40 import java.io.IOException; 41 import java.nio.ByteBuffer; 42 import java.nio.channels.AsynchronousCloseException; 43 import java.nio.channels.ClosedByInterruptException; 44 import java.nio.channels.ClosedChannelException; 45 import java.nio.channels.FileChannel; 46 import java.nio.channels.FileLock; 47 import java.nio.channels.FileLockInterruptionException; 48 import java.nio.channels.NonReadableChannelException; 49 import java.nio.channels.NonWritableChannelException; 50 import java.nio.file.OpenOption; 51 import java.util.ArrayList; 52 import java.util.List; 53 import java.util.concurrent.Callable; 54 import java.util.concurrent.CountDownLatch; 55 import java.util.concurrent.ExecutionException; 56 import java.util.concurrent.ExecutorService; 57 import java.util.concurrent.Executors; 58 import java.util.concurrent.Future; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 import org.junit.runners.JUnit4; 62 63 /** 64 * Most of the behavior of {@link JimfsFileChannel} is handled by the {@link RegularFile} 65 * implementations, so the thorough tests of that are in {@link RegularFileTest}. This mostly tests 66 * interactions with the file and channel positions. 67 * 68 * @author Colin Decker 69 */ 70 @RunWith(JUnit4.class) 71 public class JimfsFileChannelTest { 72 channel(RegularFile file, OpenOption... options)73 private static FileChannel channel(RegularFile file, OpenOption... options) throws IOException { 74 return new JimfsFileChannel( 75 file, 76 Options.getOptionsForChannel(ImmutableSet.copyOf(options)), 77 new FileSystemState(Runnables.doNothing())); 78 } 79 80 @Test testPosition()81 public void testPosition() throws IOException { 82 FileChannel channel = channel(regularFile(10), READ); 83 assertEquals(0, channel.position()); 84 assertSame(channel, channel.position(100)); 85 assertEquals(100, channel.position()); 86 } 87 88 @Test testSize()89 public void testSize() throws IOException { 90 RegularFile file = regularFile(10); 91 FileChannel channel = channel(file, READ); 92 93 assertEquals(10, channel.size()); 94 95 file.write(10, new byte[90], 0, 90); 96 assertEquals(100, channel.size()); 97 } 98 99 @Test testRead()100 public void testRead() throws IOException { 101 RegularFile file = regularFile(20); 102 FileChannel channel = channel(file, READ); 103 assertEquals(0, channel.position()); 104 105 ByteBuffer buf = buffer("1234567890"); 106 ByteBuffer buf2 = buffer("123457890"); 107 assertEquals(10, channel.read(buf)); 108 assertEquals(10, channel.position()); 109 110 buf.flip(); 111 assertEquals(10, channel.read(new ByteBuffer[] {buf, buf2})); 112 assertEquals(20, channel.position()); 113 114 buf.flip(); 115 buf2.flip(); 116 file.write(20, new byte[10], 0, 10); 117 assertEquals(10, channel.read(new ByteBuffer[] {buf, buf2}, 0, 2)); 118 assertEquals(30, channel.position()); 119 120 buf.flip(); 121 assertEquals(10, channel.read(buf, 5)); 122 assertEquals(30, channel.position()); 123 124 buf.flip(); 125 assertEquals(-1, channel.read(buf)); 126 assertEquals(30, channel.position()); 127 } 128 129 @Test testWrite()130 public void testWrite() throws IOException { 131 RegularFile file = regularFile(0); 132 FileChannel channel = channel(file, WRITE); 133 assertEquals(0, channel.position()); 134 135 ByteBuffer buf = buffer("1234567890"); 136 ByteBuffer buf2 = buffer("1234567890"); 137 assertEquals(10, channel.write(buf)); 138 assertEquals(10, channel.position()); 139 140 buf.flip(); 141 assertEquals(20, channel.write(new ByteBuffer[] {buf, buf2})); 142 assertEquals(30, channel.position()); 143 144 buf.flip(); 145 buf2.flip(); 146 assertEquals(20, channel.write(new ByteBuffer[] {buf, buf2}, 0, 2)); 147 assertEquals(50, channel.position()); 148 149 buf.flip(); 150 assertEquals(10, channel.write(buf, 5)); 151 assertEquals(50, channel.position()); 152 } 153 154 @Test testAppend()155 public void testAppend() throws IOException { 156 RegularFile file = regularFile(0); 157 FileChannel channel = channel(file, WRITE, APPEND); 158 assertEquals(0, channel.position()); 159 160 ByteBuffer buf = buffer("1234567890"); 161 ByteBuffer buf2 = buffer("1234567890"); 162 163 assertEquals(10, channel.write(buf)); 164 assertEquals(10, channel.position()); 165 166 buf.flip(); 167 channel.position(0); 168 assertEquals(20, channel.write(new ByteBuffer[] {buf, buf2})); 169 assertEquals(30, channel.position()); 170 171 buf.flip(); 172 buf2.flip(); 173 channel.position(0); 174 assertEquals(20, channel.write(new ByteBuffer[] {buf, buf2}, 0, 2)); 175 assertEquals(50, channel.position()); 176 177 buf.flip(); 178 channel.position(0); 179 assertEquals(10, channel.write(buf, 5)); 180 assertEquals(60, channel.position()); 181 182 buf.flip(); 183 channel.position(0); 184 assertEquals(10, channel.transferFrom(new ByteBufferChannel(buf), 0, 10)); 185 assertEquals(70, channel.position()); 186 } 187 188 @Test testTransferTo()189 public void testTransferTo() throws IOException { 190 RegularFile file = regularFile(10); 191 FileChannel channel = channel(file, READ); 192 193 ByteBufferChannel writeChannel = new ByteBufferChannel(buffer("1234567890")); 194 assertEquals(10, channel.transferTo(0, 100, writeChannel)); 195 assertEquals(0, channel.position()); 196 } 197 198 @Test testTransferFrom()199 public void testTransferFrom() throws IOException { 200 RegularFile file = regularFile(0); 201 FileChannel channel = channel(file, WRITE); 202 203 ByteBufferChannel readChannel = new ByteBufferChannel(buffer("1234567890")); 204 assertEquals(10, channel.transferFrom(readChannel, 0, 100)); 205 assertEquals(0, channel.position()); 206 } 207 208 @Test testTruncate()209 public void testTruncate() throws IOException { 210 RegularFile file = regularFile(10); 211 FileChannel channel = channel(file, WRITE); 212 213 channel.truncate(10); // no resize, >= size 214 assertEquals(10, file.size()); 215 channel.truncate(11); // no resize, > size 216 assertEquals(10, file.size()); 217 channel.truncate(5); // resize down to 5 218 assertEquals(5, file.size()); 219 220 channel.position(20); 221 channel.truncate(10); 222 assertEquals(10, channel.position()); 223 channel.truncate(2); 224 assertEquals(2, channel.position()); 225 } 226 227 @Test testFileTimeUpdates()228 public void testFileTimeUpdates() throws IOException { 229 RegularFile file = regularFile(10); 230 FileChannel channel = 231 new JimfsFileChannel( 232 file, 233 ImmutableSet.<OpenOption>of(READ, WRITE), 234 new FileSystemState(Runnables.doNothing())); 235 236 // accessed 237 long accessTime = file.getLastAccessTime(); 238 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 239 240 channel.read(ByteBuffer.allocate(10)); 241 assertNotEquals(accessTime, file.getLastAccessTime()); 242 243 accessTime = file.getLastAccessTime(); 244 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 245 246 channel.read(ByteBuffer.allocate(10), 0); 247 assertNotEquals(accessTime, file.getLastAccessTime()); 248 249 accessTime = file.getLastAccessTime(); 250 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 251 252 channel.read(new ByteBuffer[] {ByteBuffer.allocate(10)}); 253 assertNotEquals(accessTime, file.getLastAccessTime()); 254 255 accessTime = file.getLastAccessTime(); 256 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 257 258 channel.read(new ByteBuffer[] {ByteBuffer.allocate(10)}, 0, 1); 259 assertNotEquals(accessTime, file.getLastAccessTime()); 260 261 accessTime = file.getLastAccessTime(); 262 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 263 264 channel.transferTo(0, 10, new ByteBufferChannel(10)); 265 assertNotEquals(accessTime, file.getLastAccessTime()); 266 267 // modified 268 long modifiedTime = file.getLastModifiedTime(); 269 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 270 271 channel.write(ByteBuffer.allocate(10)); 272 assertNotEquals(modifiedTime, file.getLastModifiedTime()); 273 274 modifiedTime = file.getLastModifiedTime(); 275 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 276 277 channel.write(ByteBuffer.allocate(10), 0); 278 assertNotEquals(modifiedTime, file.getLastModifiedTime()); 279 280 modifiedTime = file.getLastModifiedTime(); 281 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 282 283 channel.write(new ByteBuffer[] {ByteBuffer.allocate(10)}); 284 assertNotEquals(modifiedTime, file.getLastModifiedTime()); 285 286 modifiedTime = file.getLastModifiedTime(); 287 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 288 289 channel.write(new ByteBuffer[] {ByteBuffer.allocate(10)}, 0, 1); 290 assertNotEquals(modifiedTime, file.getLastModifiedTime()); 291 292 modifiedTime = file.getLastModifiedTime(); 293 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 294 295 channel.truncate(0); 296 assertNotEquals(modifiedTime, file.getLastModifiedTime()); 297 298 modifiedTime = file.getLastModifiedTime(); 299 Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); 300 301 channel.transferFrom(new ByteBufferChannel(10), 0, 10); 302 assertNotEquals(modifiedTime, file.getLastModifiedTime()); 303 } 304 305 @Test testClose()306 public void testClose() throws IOException { 307 FileChannel channel = channel(regularFile(0), READ, WRITE); 308 ExecutorService executor = Executors.newSingleThreadExecutor(); 309 assertTrue(channel.isOpen()); 310 channel.close(); 311 assertFalse(channel.isOpen()); 312 313 try { 314 channel.position(); 315 fail(); 316 } catch (ClosedChannelException expected) { 317 } 318 319 try { 320 channel.position(0); 321 fail(); 322 } catch (ClosedChannelException expected) { 323 } 324 325 try { 326 channel.lock(); 327 fail(); 328 } catch (ClosedChannelException expected) { 329 } 330 331 try { 332 channel.lock(0, 10, true); 333 fail(); 334 } catch (ClosedChannelException expected) { 335 } 336 337 try { 338 channel.tryLock(); 339 fail(); 340 } catch (ClosedChannelException expected) { 341 } 342 343 try { 344 channel.tryLock(0, 10, true); 345 fail(); 346 } catch (ClosedChannelException expected) { 347 } 348 349 try { 350 channel.force(true); 351 fail(); 352 } catch (ClosedChannelException expected) { 353 } 354 355 try { 356 channel.write(buffer("111")); 357 fail(); 358 } catch (ClosedChannelException expected) { 359 } 360 361 try { 362 channel.write(buffer("111"), 10); 363 fail(); 364 } catch (ClosedChannelException expected) { 365 } 366 367 try { 368 channel.write(new ByteBuffer[] {buffer("111"), buffer("111")}); 369 fail(); 370 } catch (ClosedChannelException expected) { 371 } 372 373 try { 374 channel.write(new ByteBuffer[] {buffer("111"), buffer("111")}, 0, 2); 375 fail(); 376 } catch (ClosedChannelException expected) { 377 } 378 379 try { 380 channel.transferFrom(new ByteBufferChannel(bytes("1111")), 0, 4); 381 fail(); 382 } catch (ClosedChannelException expected) { 383 } 384 385 try { 386 channel.truncate(0); 387 fail(); 388 } catch (ClosedChannelException expected) { 389 } 390 391 try { 392 channel.read(buffer("111")); 393 fail(); 394 } catch (ClosedChannelException expected) { 395 } 396 397 try { 398 channel.read(buffer("111"), 10); 399 fail(); 400 } catch (ClosedChannelException expected) { 401 } 402 403 try { 404 channel.read(new ByteBuffer[] {buffer("111"), buffer("111")}); 405 fail(); 406 } catch (ClosedChannelException expected) { 407 } 408 409 try { 410 channel.read(new ByteBuffer[] {buffer("111"), buffer("111")}, 0, 2); 411 fail(); 412 } catch (ClosedChannelException expected) { 413 } 414 415 try { 416 channel.transferTo(0, 10, new ByteBufferChannel(buffer("111"))); 417 fail(); 418 } catch (ClosedChannelException expected) { 419 } 420 421 executor.shutdown(); 422 } 423 424 @Test testWritesInReadOnlyMode()425 public void testWritesInReadOnlyMode() throws IOException { 426 FileChannel channel = channel(regularFile(0), READ); 427 428 try { 429 channel.write(buffer("111")); 430 fail(); 431 } catch (NonWritableChannelException expected) { 432 } 433 434 try { 435 channel.write(buffer("111"), 10); 436 fail(); 437 } catch (NonWritableChannelException expected) { 438 } 439 440 try { 441 channel.write(new ByteBuffer[] {buffer("111"), buffer("111")}); 442 fail(); 443 } catch (NonWritableChannelException expected) { 444 } 445 446 try { 447 channel.write(new ByteBuffer[] {buffer("111"), buffer("111")}, 0, 2); 448 fail(); 449 } catch (NonWritableChannelException expected) { 450 } 451 452 try { 453 channel.transferFrom(new ByteBufferChannel(bytes("1111")), 0, 4); 454 fail(); 455 } catch (NonWritableChannelException expected) { 456 } 457 458 try { 459 channel.truncate(0); 460 fail(); 461 } catch (NonWritableChannelException expected) { 462 } 463 464 try { 465 channel.lock(0, 10, false); 466 fail(); 467 } catch (NonWritableChannelException expected) { 468 } 469 } 470 471 @Test testReadsInWriteOnlyMode()472 public void testReadsInWriteOnlyMode() throws IOException { 473 FileChannel channel = channel(regularFile(0), WRITE); 474 475 try { 476 channel.read(buffer("111")); 477 fail(); 478 } catch (NonReadableChannelException expected) { 479 } 480 481 try { 482 channel.read(buffer("111"), 10); 483 fail(); 484 } catch (NonReadableChannelException expected) { 485 } 486 487 try { 488 channel.read(new ByteBuffer[] {buffer("111"), buffer("111")}); 489 fail(); 490 } catch (NonReadableChannelException expected) { 491 } 492 493 try { 494 channel.read(new ByteBuffer[] {buffer("111"), buffer("111")}, 0, 2); 495 fail(); 496 } catch (NonReadableChannelException expected) { 497 } 498 499 try { 500 channel.transferTo(0, 10, new ByteBufferChannel(buffer("111"))); 501 fail(); 502 } catch (NonReadableChannelException expected) { 503 } 504 505 try { 506 channel.lock(0, 10, true); 507 fail(); 508 } catch (NonReadableChannelException expected) { 509 } 510 } 511 512 @Test testPositionNegative()513 public void testPositionNegative() throws IOException { 514 FileChannel channel = channel(regularFile(0), READ, WRITE); 515 516 try { 517 channel.position(-1); 518 fail(); 519 } catch (IllegalArgumentException expected) { 520 } 521 } 522 523 @Test testTruncateNegative()524 public void testTruncateNegative() throws IOException { 525 FileChannel channel = channel(regularFile(0), READ, WRITE); 526 527 try { 528 channel.truncate(-1); 529 fail(); 530 } catch (IllegalArgumentException expected) { 531 } 532 } 533 534 @Test testWriteNegative()535 public void testWriteNegative() throws IOException { 536 FileChannel channel = channel(regularFile(0), READ, WRITE); 537 538 try { 539 channel.write(buffer("111"), -1); 540 fail(); 541 } catch (IllegalArgumentException expected) { 542 } 543 544 ByteBuffer[] bufs = {buffer("111"), buffer("111")}; 545 try { 546 channel.write(bufs, -1, 10); 547 fail(); 548 } catch (IndexOutOfBoundsException expected) { 549 } 550 551 try { 552 channel.write(bufs, 0, -1); 553 fail(); 554 } catch (IndexOutOfBoundsException expected) { 555 } 556 } 557 558 @Test testReadNegative()559 public void testReadNegative() throws IOException { 560 FileChannel channel = channel(regularFile(0), READ, WRITE); 561 562 try { 563 channel.read(buffer("111"), -1); 564 fail(); 565 } catch (IllegalArgumentException expected) { 566 } 567 568 ByteBuffer[] bufs = {buffer("111"), buffer("111")}; 569 try { 570 channel.read(bufs, -1, 10); 571 fail(); 572 } catch (IndexOutOfBoundsException expected) { 573 } 574 575 try { 576 channel.read(bufs, 0, -1); 577 fail(); 578 } catch (IndexOutOfBoundsException expected) { 579 } 580 } 581 582 @Test testTransferToNegative()583 public void testTransferToNegative() throws IOException { 584 FileChannel channel = channel(regularFile(0), READ, WRITE); 585 586 try { 587 channel.transferTo(-1, 0, new ByteBufferChannel(10)); 588 fail(); 589 } catch (IllegalArgumentException expected) { 590 } 591 592 try { 593 channel.transferTo(0, -1, new ByteBufferChannel(10)); 594 fail(); 595 } catch (IllegalArgumentException expected) { 596 } 597 } 598 599 @Test testTransferFromNegative()600 public void testTransferFromNegative() throws IOException { 601 FileChannel channel = channel(regularFile(0), READ, WRITE); 602 603 try { 604 channel.transferFrom(new ByteBufferChannel(10), -1, 0); 605 fail(); 606 } catch (IllegalArgumentException expected) { 607 } 608 609 try { 610 channel.transferFrom(new ByteBufferChannel(10), 0, -1); 611 fail(); 612 } catch (IllegalArgumentException expected) { 613 } 614 } 615 616 @Test testLockNegative()617 public void testLockNegative() throws IOException { 618 FileChannel channel = channel(regularFile(0), READ, WRITE); 619 620 try { 621 channel.lock(-1, 10, true); 622 fail(); 623 } catch (IllegalArgumentException expected) { 624 } 625 626 try { 627 channel.lock(0, -1, true); 628 fail(); 629 } catch (IllegalArgumentException expected) { 630 } 631 632 try { 633 channel.tryLock(-1, 10, true); 634 fail(); 635 } catch (IllegalArgumentException expected) { 636 } 637 638 try { 639 channel.tryLock(0, -1, true); 640 fail(); 641 } catch (IllegalArgumentException expected) { 642 } 643 } 644 645 @Test testNullPointerExceptions()646 public void testNullPointerExceptions() throws IOException { 647 FileChannel channel = channel(regularFile(100), READ, WRITE); 648 649 NullPointerTester tester = new NullPointerTester(); 650 tester.testAllPublicInstanceMethods(channel); 651 } 652 653 @Test testLock()654 public void testLock() throws IOException { 655 FileChannel channel = channel(regularFile(10), READ, WRITE); 656 657 assertNotNull(channel.lock()); 658 assertNotNull(channel.lock(0, 10, false)); 659 assertNotNull(channel.lock(0, 10, true)); 660 661 assertNotNull(channel.tryLock()); 662 assertNotNull(channel.tryLock(0, 10, false)); 663 assertNotNull(channel.tryLock(0, 10, true)); 664 665 FileLock lock = channel.lock(); 666 assertTrue(lock.isValid()); 667 lock.release(); 668 assertFalse(lock.isValid()); 669 } 670 671 @Test testAsynchronousClose()672 public void testAsynchronousClose() throws Exception { 673 RegularFile file = regularFile(10); 674 final FileChannel channel = channel(file, READ, WRITE); 675 676 file.writeLock().lock(); // ensure all operations on the channel will block 677 678 ExecutorService executor = Executors.newCachedThreadPool(); 679 680 CountDownLatch latch = new CountDownLatch(BLOCKING_OP_COUNT); 681 List<Future<?>> futures = queueAllBlockingOperations(channel, executor, latch); 682 683 // wait for all the threads to have started running 684 latch.await(); 685 // then ensure time for operations to start blocking 686 Uninterruptibles.sleepUninterruptibly(20, MILLISECONDS); 687 688 // close channel on this thread 689 channel.close(); 690 691 // the blocking operations are running on different threads, so they all get 692 // AsynchronousCloseException 693 for (Future<?> future : futures) { 694 try { 695 future.get(); 696 fail(); 697 } catch (ExecutionException expected) { 698 assertWithMessage("blocking thread exception") 699 .that(expected.getCause()) 700 .isInstanceOf(AsynchronousCloseException.class); 701 } 702 } 703 } 704 705 @Test testCloseByInterrupt()706 public void testCloseByInterrupt() throws Exception { 707 RegularFile file = regularFile(10); 708 final FileChannel channel = channel(file, READ, WRITE); 709 710 file.writeLock().lock(); // ensure all operations on the channel will block 711 712 ExecutorService executor = Executors.newCachedThreadPool(); 713 714 final CountDownLatch threadStartLatch = new CountDownLatch(1); 715 final SettableFuture<Throwable> interruptException = SettableFuture.create(); 716 717 // This thread, being the first to run, will be blocking on the interruptible lock (the byte 718 // file's write lock) and as such will be interrupted properly... the other threads will be 719 // blocked on the lock that guards the position field and the specification that only one method 720 // on the channel will be in progress at a time. That lock is not interruptible, so we must 721 // interrupt this thread. 722 Thread thread = 723 new Thread( 724 new Runnable() { 725 @Override 726 public void run() { 727 threadStartLatch.countDown(); 728 try { 729 channel.write(ByteBuffer.allocate(20)); 730 interruptException.set(null); 731 } catch (Throwable e) { 732 interruptException.set(e); 733 } 734 } 735 }); 736 thread.start(); 737 738 // let the thread start running 739 threadStartLatch.await(); 740 // then ensure time for thread to start blocking on the write lock 741 Uninterruptibles.sleepUninterruptibly(10, MILLISECONDS); 742 743 CountDownLatch blockingStartLatch = new CountDownLatch(BLOCKING_OP_COUNT); 744 List<Future<?>> futures = queueAllBlockingOperations(channel, executor, blockingStartLatch); 745 746 // wait for all blocking threads to start 747 blockingStartLatch.await(); 748 // then ensure time for the operations to start blocking 749 Uninterruptibles.sleepUninterruptibly(20, MILLISECONDS); 750 751 // interrupting this blocking thread closes the channel and makes all the other threads 752 // throw AsynchronousCloseException... the operation on this thread should throw 753 // ClosedByInterruptException 754 thread.interrupt(); 755 756 // get the exception that caused the interrupted operation to terminate 757 assertWithMessage("interrupted thread exception") 758 .that(interruptException.get(200, MILLISECONDS)) 759 .isInstanceOf(ClosedByInterruptException.class); 760 761 // check that each other thread got AsynchronousCloseException (since the interrupt, on a 762 // different thread, closed the channel) 763 for (Future<?> future : futures) { 764 try { 765 future.get(); 766 fail(); 767 } catch (ExecutionException expected) { 768 assertWithMessage("blocking thread exception") 769 .that(expected.getCause()) 770 .isInstanceOf(AsynchronousCloseException.class); 771 } 772 } 773 } 774 775 private static final int BLOCKING_OP_COUNT = 10; 776 777 /** 778 * Queues blocking operations on the channel in separate threads using the given executor. The 779 * given latch should have a count of BLOCKING_OP_COUNT to allow the caller wants to wait for all 780 * threads to start executing. 781 */ queueAllBlockingOperations( final FileChannel channel, ExecutorService executor, final CountDownLatch startLatch)782 private List<Future<?>> queueAllBlockingOperations( 783 final FileChannel channel, ExecutorService executor, final CountDownLatch startLatch) { 784 List<Future<?>> futures = new ArrayList<>(); 785 786 final ByteBuffer buffer = ByteBuffer.allocate(10); 787 futures.add( 788 executor.submit( 789 new Callable<Object>() { 790 @Override 791 public Object call() throws Exception { 792 startLatch.countDown(); 793 channel.write(buffer); 794 return null; 795 } 796 })); 797 798 futures.add( 799 executor.submit( 800 new Callable<Object>() { 801 @Override 802 public Object call() throws Exception { 803 startLatch.countDown(); 804 channel.write(buffer, 0); 805 return null; 806 } 807 })); 808 809 futures.add( 810 executor.submit( 811 new Callable<Object>() { 812 @Override 813 public Object call() throws Exception { 814 startLatch.countDown(); 815 channel.write(new ByteBuffer[] {buffer, buffer}); 816 return null; 817 } 818 })); 819 820 futures.add( 821 executor.submit( 822 new Callable<Object>() { 823 @Override 824 public Object call() throws Exception { 825 startLatch.countDown(); 826 channel.write(new ByteBuffer[] {buffer, buffer, buffer}, 0, 2); 827 return null; 828 } 829 })); 830 831 futures.add( 832 executor.submit( 833 new Callable<Object>() { 834 @Override 835 public Object call() throws Exception { 836 startLatch.countDown(); 837 channel.read(buffer); 838 return null; 839 } 840 })); 841 842 futures.add( 843 executor.submit( 844 new Callable<Object>() { 845 @Override 846 public Object call() throws Exception { 847 startLatch.countDown(); 848 channel.read(buffer, 0); 849 return null; 850 } 851 })); 852 853 futures.add( 854 executor.submit( 855 new Callable<Object>() { 856 @Override 857 public Object call() throws Exception { 858 startLatch.countDown(); 859 channel.read(new ByteBuffer[] {buffer, buffer}); 860 return null; 861 } 862 })); 863 864 futures.add( 865 executor.submit( 866 new Callable<Object>() { 867 @Override 868 public Object call() throws Exception { 869 startLatch.countDown(); 870 channel.read(new ByteBuffer[] {buffer, buffer, buffer}, 0, 2); 871 return null; 872 } 873 })); 874 875 futures.add( 876 executor.submit( 877 new Callable<Object>() { 878 @Override 879 public Object call() throws Exception { 880 startLatch.countDown(); 881 channel.transferTo(0, 10, new ByteBufferChannel(buffer)); 882 return null; 883 } 884 })); 885 886 futures.add( 887 executor.submit( 888 new Callable<Object>() { 889 @Override 890 public Object call() throws Exception { 891 startLatch.countDown(); 892 channel.transferFrom(new ByteBufferChannel(buffer), 0, 10); 893 return null; 894 } 895 })); 896 897 return futures; 898 } 899 900 /** 901 * Tests that the methods on the default FileChannel that support InterruptibleChannel behavior 902 * also support it on JimfsFileChannel, by just interrupting the thread before calling the method. 903 */ 904 @Test testInterruptedThreads()905 public void testInterruptedThreads() throws IOException { 906 final ByteBuffer buf = ByteBuffer.allocate(10); 907 final ByteBuffer[] bufArray = {buf}; 908 909 assertClosedByInterrupt( 910 new FileChannelMethod() { 911 @Override 912 public void call(FileChannel channel) throws IOException { 913 channel.size(); 914 } 915 }); 916 917 assertClosedByInterrupt( 918 new FileChannelMethod() { 919 @Override 920 public void call(FileChannel channel) throws IOException { 921 channel.position(); 922 } 923 }); 924 925 assertClosedByInterrupt( 926 new FileChannelMethod() { 927 @Override 928 public void call(FileChannel channel) throws IOException { 929 channel.position(0); 930 } 931 }); 932 933 assertClosedByInterrupt( 934 new FileChannelMethod() { 935 @Override 936 public void call(FileChannel channel) throws IOException { 937 channel.write(buf); 938 } 939 }); 940 941 assertClosedByInterrupt( 942 new FileChannelMethod() { 943 @Override 944 public void call(FileChannel channel) throws IOException { 945 channel.write(bufArray, 0, 1); 946 } 947 }); 948 949 assertClosedByInterrupt( 950 new FileChannelMethod() { 951 @Override 952 public void call(FileChannel channel) throws IOException { 953 channel.read(buf); 954 } 955 }); 956 957 assertClosedByInterrupt( 958 new FileChannelMethod() { 959 @Override 960 public void call(FileChannel channel) throws IOException { 961 channel.read(bufArray, 0, 1); 962 } 963 }); 964 965 assertClosedByInterrupt( 966 new FileChannelMethod() { 967 @Override 968 public void call(FileChannel channel) throws IOException { 969 channel.write(buf, 0); 970 } 971 }); 972 973 assertClosedByInterrupt( 974 new FileChannelMethod() { 975 @Override 976 public void call(FileChannel channel) throws IOException { 977 channel.read(buf, 0); 978 } 979 }); 980 981 assertClosedByInterrupt( 982 new FileChannelMethod() { 983 @Override 984 public void call(FileChannel channel) throws IOException { 985 channel.transferTo(0, 1, channel(regularFile(10), READ, WRITE)); 986 } 987 }); 988 989 assertClosedByInterrupt( 990 new FileChannelMethod() { 991 @Override 992 public void call(FileChannel channel) throws IOException { 993 channel.transferFrom(channel(regularFile(10), READ, WRITE), 0, 1); 994 } 995 }); 996 997 assertClosedByInterrupt( 998 new FileChannelMethod() { 999 @Override 1000 public void call(FileChannel channel) throws IOException { 1001 channel.force(true); 1002 } 1003 }); 1004 1005 assertClosedByInterrupt( 1006 new FileChannelMethod() { 1007 @Override 1008 public void call(FileChannel channel) throws IOException { 1009 channel.truncate(0); 1010 } 1011 }); 1012 1013 assertClosedByInterrupt( 1014 new FileChannelMethod() { 1015 @Override 1016 public void call(FileChannel channel) throws IOException { 1017 channel.lock(0, 1, true); 1018 } 1019 }); 1020 1021 // tryLock() does not handle interruption 1022 // map() always throws UOE; it doesn't make sense for it to try to handle interruption 1023 } 1024 1025 private interface FileChannelMethod { call(FileChannel channel)1026 void call(FileChannel channel) throws IOException; 1027 } 1028 1029 /** 1030 * Asserts that when the given operation is run on an interrupted thread, {@code 1031 * ClosedByInterruptException} is thrown, the channel is closed and the thread is no longer 1032 * interrupted. 1033 */ assertClosedByInterrupt(FileChannelMethod method)1034 private static void assertClosedByInterrupt(FileChannelMethod method) throws IOException { 1035 FileChannel channel = channel(regularFile(10), READ, WRITE); 1036 Thread.currentThread().interrupt(); 1037 try { 1038 method.call(channel); 1039 fail( 1040 "expected the method to throw ClosedByInterruptException or " 1041 + "FileLockInterruptionException"); 1042 } catch (ClosedByInterruptException | FileLockInterruptionException expected) { 1043 assertFalse("expected the channel to be closed", channel.isOpen()); 1044 assertTrue("expected the thread to still be interrupted", Thread.interrupted()); 1045 } finally { 1046 Thread.interrupted(); // ensure the thread isn't interrupted when this method returns 1047 } 1048 } 1049 } 1050