• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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