Path: blob/master/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java
41149 views
/*1* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24*25* @test26* @modules java.base/java.io:open27* @library /test/lib28* @build jdk.test.lib.util.FileUtils UnreferencedFISClosesFd29* @bug 652406230* @summary Test to ensure that FIS.finalize() invokes the close() method as per31* the specification.32* @run main/othervm UnreferencedFISClosesFd33*/34import java.io.File;35import java.io.FileDescriptor;36import java.io.FileInputStream;37import java.io.FileNotFoundException;38import java.io.IOException;39import java.lang.management.ManagementFactory;40import java.lang.management.OperatingSystemMXBean;41import java.lang.ref.Reference;42import java.lang.ref.ReferenceQueue;43import java.lang.ref.WeakReference;44import java.lang.reflect.Field;45import java.nio.file.Path;46import java.util.ArrayDeque;47import java.util.HashSet;48import java.util.concurrent.atomic.AtomicInteger;4950import com.sun.management.UnixOperatingSystemMXBean;5152import jdk.test.lib.util.FileUtils;5354/**55* Tests for FIS unreferenced.56* - Not subclassed - cleaner cleanup57* - Subclassed no finalize or close - cleaner cleanup58* - Subclassed close overridden - AltFinalizer cleanup59* - Subclasses finalize overridden - cleaner cleanup60* - Subclasses finalize and close overridden - AltFinalizer cleanup61*/62public class UnreferencedFISClosesFd {6364static final String FILE_NAME = "empty.txt";6566/**67* Subclass w/ no overrides; not finalize or close.68* Cleanup should be via the Cleaner.69*/70public static class StreamOverrides extends FileInputStream {7172protected final AtomicInteger closeCounter;7374public StreamOverrides(String name) throws FileNotFoundException {75super(name);76closeCounter = new AtomicInteger(0);77}7879final AtomicInteger closeCounter() {80return closeCounter;81}82}8384/**85* Subclass overrides close.86* Cleanup should be via the Cleaner.87*/88public static class StreamOverridesClose extends StreamOverrides {8990public StreamOverridesClose(String name) throws FileNotFoundException {91super(name);92}9394public void close() throws IOException {95closeCounter.incrementAndGet();96super.close();97}98}99100/**101* Subclass overrides finalize.102* Cleanup should be via the Cleaner.103*/104public static class StreamOverridesFinalize extends StreamOverrides {105106public StreamOverridesFinalize(String name) throws FileNotFoundException {107super(name);108}109110@SuppressWarnings({"deprecation","removal"})111protected void finalize() throws IOException, Throwable {112super.finalize();113}114}115116/**117* Subclass overrides finalize and close.118* Cleanup should be via AltFinalizer calling close().119*/120public static class StreamOverridesFinalizeClose extends StreamOverridesClose {121122public StreamOverridesFinalizeClose(String name) throws FileNotFoundException {123super(name);124}125126@SuppressWarnings({"deprecation","removal"})127protected void finalize() throws IOException, Throwable {128super.finalize();129}130}131132/**133* Main runs each test case and reports number of failures.134*/135public static void main(String argv[]) throws Exception {136137File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME);138inFile.createNewFile();139inFile.deleteOnExit();140141String name = inFile.getPath();142143FileUtils.listFileDescriptors(System.out);144long fdCount0 = getFdCount();145146int failCount = 0;147failCount += test(new FileInputStream(name));148149failCount += test(new StreamOverrides(name));150151failCount += test(new StreamOverridesClose(name));152153failCount += test(new StreamOverridesFinalize(name));154155failCount += test(new StreamOverridesFinalizeClose(name));156157if (failCount > 0) {158throw new AssertionError("Failed test count: " + failCount);159}160161// Check the final count of open file descriptors162long fdCount = getFdCount();163if (fdCount != fdCount0) {164System.out.printf("initial count of open file descriptors: %d%n", fdCount0);165System.out.printf("final count of open file descriptors: %d%n", fdCount);166FileUtils.listFileDescriptors(System.out);167}168}169170// Get the count of open file descriptors, or -1 if not available171private static long getFdCount() {172OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();173return (mxBean instanceof UnixOperatingSystemMXBean)174? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()175: -1L;176}177178private static int test(FileInputStream fis) throws Exception {179180try {181System.out.printf("%nTesting %s%n", fis.getClass().getName());182183// Prepare to wait for FIS to be reclaimed184ReferenceQueue<Object> queue = new ReferenceQueue<>();185HashSet<Reference<?>> pending = new HashSet<>();186WeakReference<FileInputStream> msWeak = new WeakReference<>(fis, queue);187pending.add(msWeak);188189FileDescriptor fd = fis.getFD();190WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue);191pending.add(fdWeak);192193Field fdField = FileDescriptor.class.getDeclaredField("fd");194fdField.setAccessible(true);195int ffd = fdField.getInt(fd);196197Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");198cleanupField.setAccessible(true);199Object cleanup = cleanupField.get(fd);200System.out.printf(" cleanup: %s, ffd: %d, cf: %s%n", cleanup, ffd, cleanupField);201if (cleanup == null) {202throw new RuntimeException("cleanup should not be null");203}204205WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue);206pending.add(cleanupWeak);207System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n",208fdWeak, msWeak, cleanupWeak);209210AtomicInteger closeCounter = fis instanceof StreamOverrides211? ((StreamOverrides)fis).closeCounter() : null;212213Reference<?> r;214while (((r = queue.remove(1000L)) != null)215|| !pending.isEmpty()) {216System.out.printf(" r: %s, pending: %d%n",217r, pending.size());218if (r != null) {219pending.remove(r);220} else {221fis = null;222fd = null;223cleanup = null;224System.gc(); // attempt to reclaim them225}226}227Reference.reachabilityFence(fd);228Reference.reachabilityFence(fis);229Reference.reachabilityFence(cleanup);230231// Confirm the correct number of calls to close depending on the cleanup type232if (closeCounter != null && closeCounter.get() > 0) {233throw new RuntimeException("Close should not have been called: count: " + closeCounter);234}235} catch (Exception ex) {236ex.printStackTrace(System.out);237return 1;238}239return 0;240}241}242243244