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