< prev index next >

test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java

Print this page
rev 52044 : 8192939: Remove Finalize methods from FileInputStream and FileOutputStream


  45 import java.lang.reflect.Field;
  46 import java.nio.file.Path;
  47 import java.util.ArrayDeque;
  48 import java.util.HashSet;
  49 import java.util.concurrent.atomic.AtomicInteger;
  50 
  51 import com.sun.management.UnixOperatingSystemMXBean;
  52 
  53 import jdk.test.lib.util.FileUtils;
  54 
  55 /**
  56  * Tests for FIS unreferenced.
  57  *  - Not subclassed - cleaner cleanup
  58  *  - Subclassed no finalize or close - cleaner cleanup
  59  *  - Subclassed close overridden - AltFinalizer cleanup
  60  *  - Subclasses finalize overridden - cleaner cleanup
  61  *  - Subclasses finalize and close overridden - AltFinalizer cleanup
  62  */
  63 public class UnreferencedFISClosesFd {
  64 
  65     enum CleanupType {
  66         CLOSE,      // Cleanup is handled via calling close
  67         CLEANER}    // Cleanup is handled via Cleaner
  68 
  69     static final String FILE_NAME = "empty.txt";
  70 
  71     /**
  72      * Subclass w/ no overrides; not finalize or close.
  73      * Cleanup should be via the Cleaner (not close).
  74      */
  75     public static class StreamOverrides extends FileInputStream {
  76 
  77         protected final AtomicInteger closeCounter;
  78 
  79         public StreamOverrides(String name) throws FileNotFoundException {
  80             super(name);
  81             closeCounter = new AtomicInteger(0);
  82         }
  83 
  84         final AtomicInteger closeCounter() {
  85             return closeCounter;
  86         }
  87     }
  88 
  89     /**
  90      * Subclass overrides close.
  91      * Cleanup should be via AltFinalizer calling close().
  92      */
  93     public static class StreamOverridesClose extends StreamOverrides {
  94 
  95         public StreamOverridesClose(String name) throws FileNotFoundException {
  96             super(name);
  97         }
  98 
  99         public void close() throws IOException {
 100             closeCounter.incrementAndGet();
 101             super.close();
 102         }
 103     }
 104 
 105     /**
 106      * Subclass overrides finalize.
 107      * Cleanup should be via the Cleaner (not close).
 108      */
 109     public static class StreamOverridesFinalize extends StreamOverrides {
 110 
 111         public StreamOverridesFinalize(String name) throws FileNotFoundException {
 112             super(name);
 113         }
 114 
 115         @SuppressWarnings({"deprecation","removal"})
 116         protected void finalize() throws IOException {
 117             super.finalize();
 118         }
 119     }
 120 
 121     /**
 122      * Subclass overrides finalize and close.
 123      * Cleanup should be via AltFinalizer calling close().
 124      */
 125     public static class StreamOverridesFinalizeClose extends StreamOverridesClose {
 126 
 127         public StreamOverridesFinalizeClose(String name) throws FileNotFoundException {
 128             super(name);
 129         }
 130 
 131         @SuppressWarnings({"deprecation","removal"})
 132         protected void finalize() throws IOException {
 133             super.finalize();
 134         }
 135     }
 136 
 137     /**
 138      * Main runs each test case and reports number of failures.
 139      */
 140     public static void main(String argv[]) throws Exception {
 141 
 142         File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME);
 143         inFile.createNewFile();
 144         inFile.deleteOnExit();
 145 
 146         String name = inFile.getPath();
 147 
 148         FileUtils.listFileDescriptors(System.out);
 149         long fdCount0 = getFdCount();
 150 
 151         int failCount = 0;
 152         failCount += test(new FileInputStream(name), CleanupType.CLEANER);
 153 
 154         failCount += test(new StreamOverrides(name), CleanupType.CLEANER);
 155 
 156         failCount += test(new StreamOverridesClose(name), CleanupType.CLOSE);
 157 
 158         failCount += test(new StreamOverridesFinalize(name), CleanupType.CLEANER);
 159 
 160         failCount += test(new StreamOverridesFinalizeClose(name), CleanupType.CLOSE);
 161 
 162         if (failCount > 0) {
 163             throw new AssertionError("Failed test count: " + failCount);
 164         }
 165 
 166         // Check the final count of open file descriptors
 167         long fdCount = getFdCount();
 168         if (fdCount != fdCount0) {
 169             System.out.printf("initial count of open file descriptors: %d%n", fdCount0);
 170             System.out.printf("final count of open file descriptors: %d%n", fdCount);
 171             FileUtils.listFileDescriptors(System.out);
 172         }
 173     }
 174 
 175     // Get the count of open file descriptors, or -1 if not available
 176     private static long getFdCount() {
 177         OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
 178         return  (mxBean instanceof UnixOperatingSystemMXBean)
 179                 ? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
 180                 : -1L;
 181     }
 182 
 183     private static int test(FileInputStream fis, CleanupType cleanType) throws Exception {
 184 
 185         try {
 186             System.out.printf("%nTesting %s%n", fis.getClass().getName());
 187 
 188             // Prepare to wait for FIS to be reclaimed
 189             ReferenceQueue<Object> queue = new ReferenceQueue<>();
 190             HashSet<Reference<?>> pending = new HashSet<>();
 191             WeakReference<FileInputStream> msWeak = new WeakReference<>(fis, queue);
 192             pending.add(msWeak);
 193 
 194             FileDescriptor fd = fis.getFD();
 195             WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue);
 196             pending.add(fdWeak);
 197 
 198             Field fdField = FileDescriptor.class.getDeclaredField("fd");
 199             fdField.setAccessible(true);
 200             int ffd = fdField.getInt(fd);
 201 
 202             Field altFinalizerField = FileInputStream.class.getDeclaredField("altFinalizer");
 203             altFinalizerField.setAccessible(true);
 204             Object altFinalizer = altFinalizerField.get(fis);
 205             if ((altFinalizer != null) ^ (cleanType == CleanupType.CLOSE)) {
 206                 throw new RuntimeException("Unexpected AltFinalizer: " + altFinalizer
 207                 + ", for " + cleanType);
 208             }
 209 
 210             Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");
 211             cleanupField.setAccessible(true);
 212             Object cleanup = cleanupField.get(fd);
 213             System.out.printf("  cleanup: %s, alt: %s, ffd: %d, cf: %s%n",
 214                     cleanup, altFinalizer, ffd, cleanupField);
 215             if ((cleanup != null) ^ (cleanType == CleanupType.CLEANER)) {
 216                 throw new Exception("unexpected cleanup: "
 217                 + cleanup + ", for " + cleanType);
 218             }
 219             if (cleanup != null) {
 220                 WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue);
 221                 pending.add(cleanupWeak);
 222                 System.out.printf("    fdWeak: %s%n    msWeak: %s%n    cleanupWeak: %s%n",
 223                         fdWeak, msWeak, cleanupWeak);
 224             }
 225             if (altFinalizer != null) {
 226                 WeakReference<Object> altFinalizerWeak = new WeakReference<>(altFinalizer, queue);
 227                 pending.add(altFinalizerWeak);
 228                 System.out.printf("    fdWeak: %s%n    msWeak: %s%n    altFinalizerWeak: %s%n",
 229                         fdWeak, msWeak, altFinalizerWeak);
 230             }
 231 
 232             AtomicInteger closeCounter = fis instanceof StreamOverrides
 233                     ? ((StreamOverrides)fis).closeCounter() : null;
 234 
 235             Reference<?> r;
 236             while (((r = queue.remove(1000L)) != null)
 237                     || !pending.isEmpty()) {
 238                 System.out.printf("    r: %s, pending: %d%n",
 239                         r, pending.size());
 240                 if (r != null) {
 241                     pending.remove(r);
 242                 } else {
 243                     fis = null;
 244                     fd = null;
 245                     cleanup = null;
 246                     altFinalizer = null;
 247                     System.gc();  // attempt to reclaim them
 248                 }
 249             }
 250             Reference.reachabilityFence(fd);
 251             Reference.reachabilityFence(fis);
 252             Reference.reachabilityFence(cleanup);
 253             Reference.reachabilityFence(altFinalizer);
 254 
 255             // Confirm the correct number of calls to close depending on the cleanup type
 256             switch (cleanType) {
 257                 case CLEANER:
 258                     if (closeCounter != null && closeCounter.get() > 0) {
 259                         throw new RuntimeException("Close should not have been called: count: "
 260                                 + closeCounter);
 261                     }
 262                     break;
 263                 case CLOSE:
 264                     if (closeCounter == null || closeCounter.get() == 0) {
 265                         throw new RuntimeException("Close should have been called: count: 0");
 266                     }
 267                     break;
 268             }
 269         } catch (Exception ex) {
 270             ex.printStackTrace(System.out);
 271             return 1;
 272         }
 273         return 0;
 274     }
 275 }


  45 import java.lang.reflect.Field;
  46 import java.nio.file.Path;
  47 import java.util.ArrayDeque;
  48 import java.util.HashSet;
  49 import java.util.concurrent.atomic.AtomicInteger;
  50 
  51 import com.sun.management.UnixOperatingSystemMXBean;
  52 
  53 import jdk.test.lib.util.FileUtils;
  54 
  55 /**
  56  * Tests for FIS unreferenced.
  57  *  - Not subclassed - cleaner cleanup
  58  *  - Subclassed no finalize or close - cleaner cleanup
  59  *  - Subclassed close overridden - AltFinalizer cleanup
  60  *  - Subclasses finalize overridden - cleaner cleanup
  61  *  - Subclasses finalize and close overridden - AltFinalizer cleanup
  62  */
  63 public class UnreferencedFISClosesFd {
  64 




  65     static final String FILE_NAME = "empty.txt";
  66 
  67     /**
  68      * Subclass w/ no overrides; not finalize or close.
  69      * Cleanup should be via the Cleaner.
  70      */
  71     public static class StreamOverrides extends FileInputStream {
  72 
  73         protected final AtomicInteger closeCounter;
  74 
  75         public StreamOverrides(String name) throws FileNotFoundException {
  76             super(name);
  77             closeCounter = new AtomicInteger(0);
  78         }
  79 
  80         final AtomicInteger closeCounter() {
  81             return closeCounter;
  82         }
  83     }
  84 
  85     /**
  86      * Subclass overrides close.
  87      * Cleanup should be via the Cleaner.
  88      */
  89     public static class StreamOverridesClose extends StreamOverrides {
  90 
  91         public StreamOverridesClose(String name) throws FileNotFoundException {
  92             super(name);
  93         }
  94 
  95         public void close() throws IOException {
  96             closeCounter.incrementAndGet();
  97             super.close();
  98         }
  99     }
 100 
 101     /**
 102      * Subclass overrides finalize.
 103      * Cleanup should be via the Cleaner.
 104      */
 105     public static class StreamOverridesFinalize extends StreamOverrides {
 106 
 107         public StreamOverridesFinalize(String name) throws FileNotFoundException {
 108             super(name);
 109         }
 110 
 111         @SuppressWarnings({"deprecation","removal"})
 112         protected void finalize() throws IOException, Throwable {
 113             super.finalize();
 114         }
 115     }
 116 
 117     /**
 118      * Subclass overrides finalize and close.
 119      * Cleanup should be via AltFinalizer calling close().
 120      */
 121     public static class StreamOverridesFinalizeClose extends StreamOverridesClose {
 122 
 123         public StreamOverridesFinalizeClose(String name) throws FileNotFoundException {
 124             super(name);
 125         }
 126 
 127         @SuppressWarnings({"deprecation","removal"})
 128         protected void finalize() throws IOException, Throwable {
 129             super.finalize();
 130         }
 131     }
 132 
 133     /**
 134      * Main runs each test case and reports number of failures.
 135      */
 136     public static void main(String argv[]) throws Exception {
 137 
 138         File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME);
 139         inFile.createNewFile();
 140         inFile.deleteOnExit();
 141 
 142         String name = inFile.getPath();
 143 
 144         FileUtils.listFileDescriptors(System.out);
 145         long fdCount0 = getFdCount();
 146 
 147         int failCount = 0;
 148         failCount += test(new FileInputStream(name));
 149 
 150         failCount += test(new StreamOverrides(name));
 151 
 152         failCount += test(new StreamOverridesClose(name));
 153 
 154         failCount += test(new StreamOverridesFinalize(name));
 155 
 156         failCount += test(new StreamOverridesFinalizeClose(name));
 157 
 158         if (failCount > 0) {
 159             throw new AssertionError("Failed test count: " + failCount);
 160         }
 161 
 162         // Check the final count of open file descriptors
 163         long fdCount = getFdCount();
 164         if (fdCount != fdCount0) {
 165             System.out.printf("initial count of open file descriptors: %d%n", fdCount0);
 166             System.out.printf("final count of open file descriptors: %d%n", fdCount);
 167             FileUtils.listFileDescriptors(System.out);
 168         }
 169     }
 170 
 171     // Get the count of open file descriptors, or -1 if not available
 172     private static long getFdCount() {
 173         OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
 174         return  (mxBean instanceof UnixOperatingSystemMXBean)
 175                 ? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
 176                 : -1L;
 177     }
 178 
 179     private static int test(FileInputStream fis) throws Exception {
 180 
 181         try {
 182             System.out.printf("%nTesting %s%n", fis.getClass().getName());
 183 
 184             // Prepare to wait for FIS to be reclaimed
 185             ReferenceQueue<Object> queue = new ReferenceQueue<>();
 186             HashSet<Reference<?>> pending = new HashSet<>();
 187             WeakReference<FileInputStream> msWeak = new WeakReference<>(fis, queue);
 188             pending.add(msWeak);
 189 
 190             FileDescriptor fd = fis.getFD();
 191             WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue);
 192             pending.add(fdWeak);
 193 
 194             Field fdField = FileDescriptor.class.getDeclaredField("fd");
 195             fdField.setAccessible(true);
 196             int ffd = fdField.getInt(fd);
 197 








 198             Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");
 199             cleanupField.setAccessible(true);
 200             Object cleanup = cleanupField.get(fd);
 201             System.out.printf("  cleanup: %s, ffd: %d, cf: %s%n", cleanup, ffd, cleanupField);
 202             if (cleanup == null) {
 203                 throw new RuntimeException("cleanup should not be null");


 204             }
 205 
 206             WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue);
 207             pending.add(cleanupWeak);
 208             System.out.printf("    fdWeak: %s%n    msWeak: %s%n    cleanupWeak: %s%n",
 209                     fdWeak, msWeak, cleanupWeak);







 210 
 211             AtomicInteger closeCounter = fis instanceof StreamOverrides
 212                     ? ((StreamOverrides)fis).closeCounter() : null;
 213 
 214             Reference<?> r;
 215             while (((r = queue.remove(1000L)) != null)
 216                     || !pending.isEmpty()) {
 217                 System.out.printf("    r: %s, pending: %d%n",
 218                         r, pending.size());
 219                 if (r != null) {
 220                     pending.remove(r);
 221                 } else {
 222                     fis = null;
 223                     fd = null;
 224                     cleanup = null;

 225                     System.gc();  // attempt to reclaim them
 226                 }
 227             }
 228             Reference.reachabilityFence(fd);
 229             Reference.reachabilityFence(fis);
 230             Reference.reachabilityFence(cleanup);

 231 
 232             // Confirm the correct number of calls to close depending on the cleanup type


 233             if (closeCounter != null && closeCounter.get() > 0) {
 234                 throw new RuntimeException("Close should not have been called: count: " + closeCounter);








 235             }
 236         } catch (Exception ex) {
 237             ex.printStackTrace(System.out);
 238             return 1;
 239         }
 240         return 0;
 241     }
 242 }
< prev index next >