Sharing.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 7105952 6322678 7082769 27 * @summary Improve finalisation for FileInputStream/FileOutputStream/RandomAccessFile 28 * @run main/othervm Sharing 29 */ 30 31import java.io.*; 32import java.nio.channels.FileChannel; 33import java.nio.channels.FileLock; 34import java.util.concurrent.CountDownLatch; 35 36public class Sharing { 37 38 static final int numFiles = 10; 39 static volatile boolean fail; 40 41 public static void main(String[] args) throws Exception { 42 TestFinalizer(); 43 TestMultipleFD(); 44 TestIsValid(); 45 MultiThreadedFD(); 46 TestCloseAll(); 47 } 48 49 /** 50 * Finalizer shouldn't discard a file descriptor until all streams have 51 * finished with it. 52 */ 53 private static void TestFinalizer() throws Exception { 54 FileDescriptor fd = null; 55 File tempFile = new File("TestFinalizer1.txt"); 56 tempFile.deleteOnExit(); 57 try (Writer writer = new FileWriter(tempFile)) { 58 for (int i=0; i<5; i++) { 59 writer.write("test file content test file content"); 60 } 61 } 62 63 FileInputStream fis1 = new FileInputStream(tempFile); 64 fd = fis1.getFD(); 65 // Create a new FIS based on the existing FD (so the two FIS's share the same native fd) 66 try (FileInputStream fis2 = new FileInputStream(fd)) { 67 // allow fis1 to be gc'ed 68 fis1 = null; 69 int ret = 0; 70 while(ret >= 0) { 71 // encourage gc 72 System.gc(); 73 // read from fis2 - when fis1 is gc'ed and finalizer is run, read will fail 74 System.out.print("."); 75 ret = fis2.read(); 76 } 77 } 78 79 // variation of above. Use RandomAccessFile to obtain a filedescriptor 80 File testFinalizerFile = new File("TestFinalizer"); 81 RandomAccessFile raf = new RandomAccessFile(testFinalizerFile, "rw"); 82 raf.writeBytes("test file content test file content"); 83 raf.seek(0L); 84 fd = raf.getFD(); 85 try (FileInputStream fis3 = new FileInputStream(fd)) { 86 // allow raf to be gc'ed 87 raf = null; 88 int ret = 0; 89 while (ret >= 0) { 90 // encourage gc 91 System.gc(); 92 /* 93 * read from fis3 - when raf is gc'ed and finalizer is run, 94 * fd should still be valid. 95 */ 96 System.out.print("."); 97 ret = fis3.read(); 98 } 99 } finally { 100 testFinalizerFile.delete(); 101 } 102 } 103 104 /** 105 * Exercise FileDispatcher close()/preClose() 106 */ 107 private static void TestMultipleFD() throws Exception { 108 RandomAccessFile raf = null; 109 FileOutputStream fos = null; 110 FileInputStream fis = null; 111 FileChannel fc = null; 112 FileLock fileLock = null; 113 114 File test1 = new File("test1"); 115 try { 116 raf = new RandomAccessFile(test1, "rw"); 117 fos = new FileOutputStream(raf.getFD()); 118 fis = new FileInputStream(raf.getFD()); 119 fc = raf.getChannel(); 120 fileLock = fc.lock(); 121 raf.setLength(0L); 122 fos.flush(); 123 fos.write("TEST".getBytes()); 124 } finally { 125 if (fileLock != null) fileLock.release(); 126 if (fis != null) fis.close(); 127 if (fos != null) fos.close(); 128 if (raf != null) raf.close(); 129 test1.delete(); 130 } 131 132 /* 133 * Close out in different order to ensure FD is not 134 * closed out too early 135 */ 136 File test2 = new File("test2"); 137 try { 138 raf = new RandomAccessFile(test2, "rw"); 139 fos = new FileOutputStream(raf.getFD()); 140 fis = new FileInputStream(raf.getFD()); 141 fc = raf.getChannel(); 142 fileLock = fc.lock(); 143 raf.setLength(0L); 144 fos.flush(); 145 fos.write("TEST".getBytes()); 146 } finally { 147 if (fileLock != null) fileLock.release(); 148 if (raf != null) raf.close(); 149 if (fos != null) fos.close(); 150 if (fis != null) fis.close(); 151 test2.delete(); 152 } 153 154 // one more time, fos first this time 155 File test3 = new File("test3"); 156 try { 157 raf = new RandomAccessFile(test3, "rw"); 158 fos = new FileOutputStream(raf.getFD()); 159 fis = new FileInputStream(raf.getFD()); 160 fc = raf.getChannel(); 161 fileLock = fc.lock(); 162 raf.setLength(0L); 163 fos.flush(); 164 fos.write("TEST".getBytes()); 165 } finally { 166 if (fileLock != null) fileLock.release(); 167 if (fos != null) fos.close(); 168 if (raf != null) raf.close(); 169 if (fis != null) fis.close(); 170 test3.delete(); 171 } 172 } 173 174 /** 175 * Similar to TestMultipleFD() but this time we 176 * just get and use FileDescriptor.valid() for testing. 177 */ 178 private static void TestIsValid() throws Exception { 179 FileDescriptor fd = null; 180 RandomAccessFile raf = null; 181 FileOutputStream fos = null; 182 FileInputStream fis = null; 183 FileChannel fc = null; 184 185 File test1 = new File("test1"); 186 try { 187 raf = new RandomAccessFile(test1, "rw"); 188 fd = raf.getFD(); 189 fos = new FileOutputStream(fd); 190 fis = new FileInputStream(fd); 191 } finally { 192 try { 193 if (fis != null) fis.close(); 194 if (fd.valid()) { 195 throw new RuntimeException("[FIS close()] FileDescriptor shouldn't be valid"); 196 } 197 if (fos != null) fos.close(); 198 if (raf != null) raf.close(); 199 } finally { 200 test1.delete(); 201 } 202 } 203 204 /* 205 * Close out in different order to ensure FD is 206 * closed correctly. 207 */ 208 File test2 = new File("test2"); 209 try { 210 raf = new RandomAccessFile(test2, "rw"); 211 fd = raf.getFD(); 212 fos = new FileOutputStream(fd); 213 fis = new FileInputStream(fd); 214 } finally { 215 try { 216 if (raf != null) raf.close(); 217 if (fd.valid()) { 218 throw new RuntimeException("[RAF close()] FileDescriptor shouldn't be valid"); 219 } 220 if (fos != null) fos.close(); 221 if (fis != null) fis.close(); 222 } finally { 223 test2.delete(); 224 } 225 } 226 227 // one more time, fos first this time 228 File test3 = new File("test3"); 229 try { 230 raf = new RandomAccessFile(test3, "rw"); 231 fd = raf.getFD(); 232 fos = new FileOutputStream(fd); 233 fis = new FileInputStream(fd); 234 } finally { 235 try { 236 if (fos != null) fos.close(); 237 if (fd.valid()) { 238 throw new RuntimeException("[FOS close()] FileDescriptor shouldn't be valid"); 239 } 240 if (raf != null) raf.close(); 241 if (fis != null) fis.close(); 242 } finally { 243 test3.delete(); 244 } 245 } 246 } 247 248 /** 249 * Test concurrent access to the same FileDescriptor 250 */ 251 private static void MultiThreadedFD() throws Exception { 252 RandomAccessFile raf = null; 253 FileDescriptor fd = null; 254 int numThreads = 2; 255 CountDownLatch done = new CountDownLatch(numThreads); 256 OpenClose[] fileOpenClose = new OpenClose[numThreads]; 257 File MultipleThreadedFD = new File("MultipleThreadedFD"); 258 try { 259 raf = new RandomAccessFile(MultipleThreadedFD, "rw"); 260 fd = raf.getFD(); 261 for(int count=0;count<numThreads;count++) { 262 fileOpenClose[count] = new OpenClose(fd, done); 263 fileOpenClose[count].start(); 264 } 265 done.await(); 266 } finally { 267 try { 268 if(raf != null) raf.close(); 269 // fd should now no longer be valid 270 if(fd.valid()) { 271 throw new RuntimeException("FileDescriptor should not be valid"); 272 } 273 // OpenClose thread tests failed 274 if(fail) { 275 throw new RuntimeException("OpenClose thread tests failed."); 276 } 277 } finally { 278 MultipleThreadedFD.delete(); 279 } 280 } 281 } 282 283 /** 284 * Test closeAll handling in FileDescriptor 285 */ 286 private static void TestCloseAll() throws Exception { 287 File testFile = new File("test"); 288 testFile.deleteOnExit(); 289 RandomAccessFile raf = new RandomAccessFile(testFile, "rw"); 290 FileInputStream fis = new FileInputStream(raf.getFD()); 291 fis.close(); 292 if (raf.getFD().valid()) { 293 throw new RuntimeException("FD should not be valid."); 294 } 295 296 // Test the suppressed exception handling - FileInputStream 297 298 raf = new RandomAccessFile(testFile, "rw"); 299 fis = new FileInputStream(raf.getFD()); 300 BadFileInputStream bfis1 = new BadFileInputStream(raf.getFD()); 301 BadFileInputStream bfis2 = new BadFileInputStream(raf.getFD()); 302 BadFileInputStream bfis3 = new BadFileInputStream(raf.getFD()); 303 // extra test - set bfis3 to null 304 bfis3 = null; 305 try { 306 fis.close(); 307 } catch (IOException ioe) { 308 ioe.printStackTrace(); 309 if (ioe.getSuppressed().length != 2) { 310 throw new RuntimeException("[FIS]Incorrect number of suppressed " + 311 "exceptions received : " + ioe.getSuppressed().length); 312 } 313 } 314 if (raf.getFD().valid()) { 315 // we should still have closed the FD 316 // even with the exception. 317 throw new RuntimeException("[FIS]TestCloseAll : FD still valid."); 318 } 319 320 // Now test with FileOutputStream 321 322 raf = new RandomAccessFile(testFile, "rw"); 323 FileOutputStream fos = new FileOutputStream(raf.getFD()); 324 BadFileOutputStream bfos1 = new BadFileOutputStream(raf.getFD()); 325 BadFileOutputStream bfos2 = new BadFileOutputStream(raf.getFD()); 326 BadFileOutputStream bfos3 = new BadFileOutputStream(raf.getFD()); 327 // extra test - set bfos3 to null 328 bfos3 = null; 329 try { 330 fos.close(); 331 } catch (IOException ioe) { 332 ioe.printStackTrace(); 333 if (ioe.getSuppressed().length != 2) { 334 throw new RuntimeException("[FOS]Incorrect number of suppressed " + 335 "exceptions received : " + ioe.getSuppressed().length); 336 } 337 } 338 if (raf.getFD().valid()) { 339 // we should still have closed the FD 340 // even with the exception. 341 throw new RuntimeException("[FOS]TestCloseAll : FD still valid."); 342 } 343 } 344 345 /** 346 * A thread which will open and close a number of FileInputStreams and 347 * FileOutputStreams referencing the same native file descriptor. 348 */ 349 private static class OpenClose extends Thread { 350 private FileDescriptor fd = null; 351 private CountDownLatch done; 352 FileInputStream[] fisArray = new FileInputStream[numFiles]; 353 FileOutputStream[] fosArray = new FileOutputStream[numFiles]; 354 355 OpenClose(FileDescriptor filedescriptor, CountDownLatch done) { 356 this.fd = filedescriptor; 357 this.done = done; 358 } 359 360 public void run() { 361 try { 362 for(int i=0;i<numFiles;i++) { 363 fisArray[i] = new FileInputStream(fd); 364 fosArray[i] = new FileOutputStream(fd); 365 } 366 367 // Now close out 368 for(int i=0;i<numFiles;i++) { 369 if(fisArray[i] != null) fisArray[i].close(); 370 if(fosArray[i] != null) fosArray[i].close(); 371 } 372 373 } catch(IOException ioe) { 374 System.out.println("OpenClose encountered IO issue :" + ioe); 375 fail = true; 376 } finally { 377 if (fd.valid()) { // fd should not be valid after first close() call 378 System.out.println("OpenClose: FileDescriptor shouldn't be valid"); 379 fail = true; 380 } 381 done.countDown(); 382 } 383 } 384 } 385 386 private static class BadFileInputStream extends FileInputStream { 387 388 BadFileInputStream(FileDescriptor fd) { 389 super(fd); 390 } 391 392 public void close() throws IOException { 393 throw new IOException("Bad close operation"); 394 } 395 } 396 397 private static class BadFileOutputStream extends FileOutputStream { 398 399 BadFileOutputStream(FileDescriptor fd) { 400 super(fd); 401 } 402 403 public void close() throws IOException { 404 throw new IOException("Bad close operation"); 405 } 406 } 407 408} 409