Basic.java revision 893:f06f30b29f36
1/*
2 * Copyright 2008-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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/* @test
25 * @bug 4607272
26 * @summary Unit test for AsynchronousFileChannel
27 */
28
29import java.nio.file.*;
30import java.nio.channels.*;
31import java.nio.ByteBuffer;
32import java.io.File;
33import java.io.IOException;
34import java.util.*;
35import java.util.concurrent.*;
36import java.util.concurrent.atomic.AtomicReference;
37import static java.nio.file.StandardOpenOption.*;
38
39public class Basic {
40
41    private static final Random rand = new Random();
42
43    public static void main(String[] args) throws IOException {
44        // create temporary file
45        File blah = File.createTempFile("blah", null);
46        blah.deleteOnExit();
47
48        final AsynchronousFileChannel ch = AsynchronousFileChannel
49            .open(blah.toPath(), READ, WRITE);
50
51        // run tests
52        testUsingCompletionHandlers(ch);
53        testUsingWaitOnResult(ch);
54        testLocking(ch);
55        testInterruptHandlerThread(ch);
56
57        // close channel and invoke test that expects channel to be closed
58        ch.close();
59        testClosedChannel(ch);
60
61        // these tests open the file themselves
62        testCustomThreadPool(blah.toPath());
63        testAsynchronousClose(blah.toPath());
64        testCancel(blah.toPath());
65        testTruncate(blah.toPath());
66    }
67
68    /*
69     * Generate buffer with random contents
70     * Writes buffer to file using a CompletionHandler to consume the result
71     *    of each write operation
72     * Reads file to EOF to a new buffer using a CompletionHandler to consume
73     *    the result of each read operation
74     * Compares buffer contents
75     */
76    static void testUsingCompletionHandlers(AsynchronousFileChannel ch)
77        throws IOException
78    {
79        System.out.println("testUsingCompletionHandlers");
80
81        ch.truncate(0L);
82
83        // generate buffer with random elements and write it to file
84        ByteBuffer src = genBuffer();
85        writeFully(ch, src, 0L);
86
87        // read to EOF or buffer is full
88        ByteBuffer dst = (rand.nextBoolean()) ?
89            ByteBuffer.allocateDirect(src.capacity()) :
90            ByteBuffer.allocate(src.capacity());
91        readAll(ch, dst, 0L);
92
93        // check buffers are the same
94        src.flip();
95        dst.flip();
96        if (!src.equals(dst)) {
97            throw new RuntimeException("Contents differ");
98        }
99    }
100
101    /*
102     * Generate buffer with random contents
103     * Writes buffer to file, invoking the Future's get method to wait for
104     *    each write operation to complete
105     * Reads file to EOF to a new buffer, invoking the Future's get method to
106     *    wait for each write operation to complete
107     * Compares buffer contents
108     */
109    static void testUsingWaitOnResult(AsynchronousFileChannel ch)
110        throws IOException
111    {
112        System.out.println("testUsingWaitOnResult");
113
114        ch.truncate(0L);
115
116        // generate buffer
117        ByteBuffer src = genBuffer();
118
119        // write buffer completely to file
120        long position = 0L;
121        while (src.hasRemaining()) {
122            Future<Integer> result = ch.write(src, position);
123            try {
124                int n = result.get();
125                // update position
126                position += n;
127            } catch (ExecutionException x) {
128                throw new RuntimeException(x.getCause());
129            } catch (InterruptedException x) {
130                throw new RuntimeException(x);
131            }
132        }
133
134        // read file into new buffer
135        ByteBuffer dst = (rand.nextBoolean()) ?
136            ByteBuffer.allocateDirect(src.capacity()) :
137            ByteBuffer.allocate(src.capacity());
138        position = 0L;
139        int n;
140        do {
141            Future<Integer> result = ch.read(dst, position);
142            try {
143                n = result.get();
144
145                // update position
146                if (n > 0) position += n;
147            } catch (ExecutionException x) {
148                throw new RuntimeException(x.getCause());
149            } catch (InterruptedException x) {
150                throw new RuntimeException(x);
151            }
152        } while (n > 0);
153
154        // check buffers are the same
155        src.flip();
156        dst.flip();
157        if (!src.equals(dst)) {
158            throw new RuntimeException("Contents differ");
159        }
160    }
161
162    // exercise lock methods
163    static void testLocking(AsynchronousFileChannel ch)
164        throws IOException
165    {
166        System.out.println("testLocking");
167
168        // test 1 - acquire lock and check that tryLock throws
169        // OverlappingFileLockException
170        FileLock fl;
171        try {
172            fl = ch.lock().get();
173        } catch (ExecutionException x) {
174            throw new RuntimeException(x);
175        } catch (InterruptedException x) {
176            throw new RuntimeException("Should not be interrupted");
177        }
178        if (!fl.acquiredBy().equals(ch))
179            throw new RuntimeException("FileLock#acquiredBy returned incorrect channel");
180        try {
181            ch.tryLock();
182            throw new RuntimeException("OverlappingFileLockException expected");
183        } catch (OverlappingFileLockException x) {
184        }
185        fl.release();
186
187        // test 2 - acquire try and check that lock throws OverlappingFileLockException
188        fl = ch.tryLock();
189        if (fl == null)
190            throw new RuntimeException("Unable to acquire lock");
191        try {
192            ch.lock(null, new CompletionHandler<FileLock,Void> () {
193                public void completed(FileLock result, Void att) {
194                }
195                public void failed(Throwable exc, Void att) {
196                }
197                public void cancelled(Void att) {
198                }
199            });
200            throw new RuntimeException("OverlappingFileLockException expected");
201        } catch (OverlappingFileLockException x) {
202        }
203        fl.release();
204    }
205
206    // interrupt should not close channel
207    static void testInterruptHandlerThread(final AsynchronousFileChannel ch) {
208        System.out.println("testInterruptHandlerThread");
209
210        ByteBuffer buf = ByteBuffer.allocateDirect(100);
211        final CountDownLatch latch = new CountDownLatch(1);
212
213        ch.read(buf, 0L, null, new CompletionHandler<Integer,Void>() {
214            public void completed(Integer result, Void att) {
215                try {
216                    Thread.currentThread().interrupt();
217                    long size = ch.size();
218                    latch.countDown();
219                } catch (IOException x) {
220                    x.printStackTrace();
221                }
222            }
223            public void failed(Throwable exc, Void att) {
224            }
225            public void cancelled(Void att) {
226            }
227        });
228
229        // wait for handler to complete
230        await(latch);
231    }
232
233    // invoke method on closed channel
234    static void testClosedChannel(AsynchronousFileChannel ch) {
235        System.out.println("testClosedChannel");
236
237        if (ch.isOpen())
238            throw new RuntimeException("Channel should be closed");
239
240        ByteBuffer buf = ByteBuffer.allocateDirect(100);
241
242        // check read fails with ClosedChannelException
243        try {
244            ch.read(buf, 0L).get();
245            throw new RuntimeException("ExecutionException expected");
246        } catch (ExecutionException x) {
247            if (!(x.getCause() instanceof ClosedChannelException))
248                throw new RuntimeException("Cause of ClosedChannelException expected");
249        } catch (InterruptedException x) {
250        }
251
252        // check write fails with ClosedChannelException
253        try {
254            ch.write(buf, 0L).get();
255            throw new RuntimeException("ExecutionException expected");
256        } catch (ExecutionException x) {
257            if (!(x.getCause() instanceof ClosedChannelException))
258                throw new RuntimeException("Cause of ClosedChannelException expected");
259        } catch (InterruptedException x) {
260        }
261
262        // check lock fails with ClosedChannelException
263        try {
264            ch.lock().get();
265            throw new RuntimeException("ExecutionException expected");
266        } catch (ExecutionException x) {
267            if (!(x.getCause() instanceof ClosedChannelException))
268                throw new RuntimeException("Cause of ClosedChannelException expected");
269        } catch (InterruptedException x) {
270        }
271    }
272
273
274    // exercise custom thread pool
275    static void testCustomThreadPool(Path file) throws IOException {
276        System.out.println("testCustomThreadPool");
277
278        // records threads that are created
279        final List<Thread> threads = new ArrayList<Thread>();
280
281        ThreadFactory threadFactory = new ThreadFactory() {
282             @Override
283             public Thread newThread(Runnable r) {
284                 Thread t = new Thread(r);
285                 t.setDaemon(true);
286                 synchronized (threads) {
287                     threads.add(t);
288                 }
289                 return t;
290             }
291        };
292
293        // exercise tests with varied number of threads
294        for (int nThreads=1; nThreads<=5; nThreads++) {
295            synchronized (threads) {
296                threads.clear();
297            }
298            ExecutorService executor = Executors.newFixedThreadPool(nThreads, threadFactory);
299            Set<StandardOpenOption> opts = EnumSet.of(WRITE);
300            AsynchronousFileChannel ch = AsynchronousFileChannel.open(file, opts, executor);
301            try {
302                for (int i=0; i<10; i++) {
303                    // do I/O operation to see which thread invokes the completion handler
304                    final AtomicReference<Thread> invoker = new AtomicReference<Thread>();
305                    final CountDownLatch latch = new CountDownLatch(1);
306
307                    ch.write(genBuffer(), 0L, null, new CompletionHandler<Integer,Void>() {
308                        public void completed(Integer result, Void att) {
309                            invoker.set(Thread.currentThread());
310                            latch.countDown();
311                        }
312                        public void failed(Throwable exc, Void att) {
313                        }
314                        public void cancelled(Void att) {
315                        }
316                    });
317                    await(latch);
318
319                    // check invoker
320                    boolean found = false;
321                    synchronized (threads) {
322                        for (Thread t: threads) {
323                            if (t == invoker.get()) {
324                                found = true;
325                                break;
326                            }
327                        }
328                    }
329                    if (!found)
330                        throw new RuntimeException("Invoker thread not found");
331                }
332            } finally {
333                ch.close();
334            }
335        }
336    }
337
338    // exercise asynchronous close
339    static void testAsynchronousClose(Path file) throws IOException {
340        System.out.println("testAsynchronousClose");
341
342        // create file
343        AsynchronousFileChannel ch = AsynchronousFileChannel
344            .open(file, WRITE, TRUNCATE_EXISTING);
345        long size = 0L;
346        do {
347            ByteBuffer buf = genBuffer();
348            int n = buf.remaining();
349            writeFully(ch, buf, size);
350            size += n;
351        } while (size < (50L * 1024L * 1024L));
352
353        ch.close();
354
355        ch = AsynchronousFileChannel.open(file, WRITE, SYNC);
356
357        // randomize number of writers, buffer size, and positions
358
359        int nwriters = 1 + rand.nextInt(8);
360        ByteBuffer[] buf = new ByteBuffer[nwriters];
361        long[] position = new long[nwriters];
362        for (int i=0; i<nwriters; i++) {
363            buf[i] = genBuffer();
364            position[i] = rand.nextInt((int)size);
365        }
366
367        // initiate I/O
368        Future[] result = new Future[nwriters];
369        for (int i=0; i<nwriters; i++) {
370            result[i] = ch.write(buf[i], position[i]);
371        }
372
373        // close file
374        ch.close();
375
376        // write operations should complete or fail with AsynchronousCloseException
377        for (int i=0; i<nwriters; i++) {
378            try {
379                result[i].get();
380            } catch (ExecutionException x) {
381                Throwable cause = x.getCause();
382                if (!(cause instanceof AsynchronousCloseException))
383                    throw new RuntimeException(cause);
384            } catch (CancellationException  x) {
385                throw new RuntimeException(x);   // should not happen
386            } catch (InterruptedException x) {
387                throw new RuntimeException(x);   // should not happen
388            }
389        }
390    }
391
392    // exercise cancel method
393    static void testCancel(Path file) throws IOException {
394        System.out.println("testCancel");
395
396        for (int i=0; i<2; i++) {
397            boolean mayInterruptIfRunning = (i == 0) ? false : true;
398
399            // open with SYNC option to improve chances that write will not
400            // complete immediately
401            AsynchronousFileChannel ch = AsynchronousFileChannel
402                .open(file, WRITE, SYNC);
403
404            // start write operation
405            final CountDownLatch latch = new CountDownLatch(1);
406            Future<Integer> res = ch.write(genBuffer(), 0L, null,
407                new CompletionHandler<Integer,Void>() {
408                    public void completed(Integer result, Void att) {
409                    }
410                    public void failed(Throwable exc, Void att) {
411                    }
412                    public void cancelled(Void att) {
413                        latch.countDown();
414                    }
415            });
416
417            // cancel operation
418            boolean cancelled = res.cancel(mayInterruptIfRunning);
419
420            // check post-conditions
421            if (!res.isDone())
422                throw new RuntimeException("isDone should return true");
423            if (res.isCancelled() != cancelled)
424                throw new RuntimeException("isCancelled not consistent");
425            try {
426                res.get();
427                if (!cancelled)
428                    throw new RuntimeException("CancellationException expected");
429            } catch (CancellationException x) {
430                // expected
431            } catch (ExecutionException x) {
432                throw new RuntimeException(x);
433            } catch (InterruptedException x) {
434                throw new RuntimeException(x);
435            }
436            try {
437                res.get(1, TimeUnit.SECONDS);
438                throw new RuntimeException("CancellationException expected");
439            } catch (CancellationException x) {
440                // expected
441            } catch (ExecutionException x) {
442                throw new RuntimeException(x);
443            } catch (TimeoutException x) {
444                throw new RuntimeException(x);
445            } catch (InterruptedException x) {
446                throw new RuntimeException(x);
447            }
448
449            // check that cancelled method is invoked
450            if (cancelled)
451                await(latch);
452
453            ch.close();
454        }
455    }
456
457    // exercise truncate method
458    static void testTruncate(Path file) throws IOException {
459        System.out.println("testTruncate");
460
461        // basic tests
462        AsynchronousFileChannel ch = AsynchronousFileChannel
463            .open(file, CREATE, WRITE, TRUNCATE_EXISTING);
464        try {
465            writeFully(ch, genBuffer(), 0L);
466            long size = ch.size();
467
468            // attempt to truncate to a size greater than the current size
469            if (ch.truncate(size + 1L).size() != size)
470                throw new RuntimeException("Unexpected size after truncation");
471
472            // truncate file
473            if (ch.truncate(size - 1L).size() != (size - 1L))
474                throw new RuntimeException("Unexpected size after truncation");
475
476            // invalid size
477            try {
478                ch.truncate(-1L);
479                throw new RuntimeException("IllegalArgumentException expected");
480            } catch (IllegalArgumentException e) { }
481
482        } finally {
483            ch.close();
484        }
485
486        // channel is closed
487        try {
488            ch.truncate(0L);
489            throw new RuntimeException("ClosedChannelException expected");
490        } catch (ClosedChannelException  e) { }
491
492        // channel is read-only
493        ch = AsynchronousFileChannel.open(file, READ);
494        try {
495            try {
496            ch.truncate(0L);
497                throw new RuntimeException("NonWritableChannelException expected");
498            } catch (NonWritableChannelException  e) { }
499        } finally {
500            ch.close();
501        }
502    }
503
504    // returns ByteBuffer with random bytes
505    static ByteBuffer genBuffer() {
506        int size = 1024 + rand.nextInt(16000);
507        byte[] buf = new byte[size];
508        boolean useDirect = rand.nextBoolean();
509        if (useDirect) {
510            ByteBuffer bb = ByteBuffer.allocateDirect(buf.length);
511            bb.put(buf);
512            bb.flip();
513            return bb;
514        } else {
515            return ByteBuffer.wrap(buf);
516        }
517    }
518
519    // writes all remaining bytes in the buffer to the given channel at the
520    // given position
521    static void writeFully(final AsynchronousFileChannel ch,
522                           final ByteBuffer src,
523                           long position)
524    {
525        final CountDownLatch latch = new CountDownLatch(1);
526
527        // use position as attachment
528        ch.write(src, position, position, new CompletionHandler<Integer,Long>() {
529            public void completed(Integer result, Long position) {
530                int n = result;
531                if (src.hasRemaining()) {
532                    long p = position + n;
533                    ch.write(src, p, p, this);
534                } else {
535                    latch.countDown();
536                }
537            }
538            public void failed(Throwable exc, Long position) {
539            }
540            public void cancelled(Long position) {
541            }
542        });
543
544        // wait for writes to complete
545        await(latch);
546    }
547
548    static void readAll(final AsynchronousFileChannel ch,
549                        final ByteBuffer dst,
550                       long position)
551    {
552        final CountDownLatch latch = new CountDownLatch(1);
553
554        // use position as attachment
555        ch.read(dst, position, position, new CompletionHandler<Integer,Long>() {
556            public void completed(Integer result, Long position) {
557                int n = result;
558                if (n > 0) {
559                    long p = position + n;
560                    ch.read(dst, p, p, this);
561                } else {
562                    latch.countDown();
563                }
564            }
565            public void failed(Throwable exc, Long position) {
566            }
567            public void cancelled(Long position) {
568            }
569        });
570
571        // wait for reads to complete
572        await(latch);
573    }
574
575    static void await(CountDownLatch latch) {
576        // wait until done
577        boolean done = false;
578        while (!done) {
579            try {
580                latch.await();
581                done = true;
582            } catch (InterruptedException x) { }
583        }
584    }
585}
586