Path: blob/master/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.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 UnreferencedFOSClosesFd29* @bug 652406230* @summary Test to ensure that the fd is closed if left unreferenced31* @run main/othervm UnreferencedFOSClosesFd32*/33import java.io.File;34import java.io.FileDescriptor;35import java.io.FileNotFoundException;36import java.io.FileOutputStream;37import java.io.IOException;38import java.lang.management.ManagementFactory;39import java.lang.management.OperatingSystemMXBean;40import java.lang.ref.Reference;41import java.lang.ref.ReferenceQueue;42import java.lang.ref.WeakReference;43import java.lang.reflect.Field;44import java.nio.file.Path;45import java.util.ArrayDeque;46import java.util.HashSet;47import java.util.concurrent.atomic.AtomicInteger;4849import com.sun.management.UnixOperatingSystemMXBean;5051import jdk.test.lib.util.FileUtils;5253public class UnreferencedFOSClosesFd {5455static final String FILE_NAME = "empty.txt";5657/**58* Subclass w/ no overrides; not close.59* Cleanup should be via the Cleaner.60*/61public static class StreamOverrides extends FileOutputStream {6263protected final AtomicInteger closeCounter;6465public StreamOverrides(String name) throws FileNotFoundException {66super(name);67closeCounter = new AtomicInteger(0);68}6970final AtomicInteger closeCounter() {71return closeCounter;72}73}7475/**76* Subclass overrides close.77* Cleanup should be via AltFinalizer calling close().78*/79public static class StreamOverridesClose extends StreamOverrides {8081public StreamOverridesClose(String name) throws FileNotFoundException {82super(name);83}8485public void close() throws IOException {86closeCounter.incrementAndGet();87super.close();88}89}9091/**92* Subclass overrides finalize and close.93* Cleanup should be via the Cleaner.94*/95public static class StreamOverridesFinalize extends StreamOverrides {9697public StreamOverridesFinalize(String name) throws FileNotFoundException {98super(name);99}100101@SuppressWarnings({"deprecation","removal"})102protected void finalize() throws IOException, Throwable {103super.finalize();104}105}106107/**108* Subclass overrides finalize and close.109* Cleanup should be via the Cleaner.110*/111public static class StreamOverridesFinalizeClose extends StreamOverridesClose {112113public StreamOverridesFinalizeClose(String name) throws FileNotFoundException {114super(name);115}116117@SuppressWarnings({"deprecation","removal"})118protected void finalize() throws IOException, Throwable {119super.finalize();120}121}122123/**124* Main runs each test case and reports number of failures.125*/126public static void main(String argv[]) throws Exception {127128File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME);129inFile.createNewFile();130inFile.deleteOnExit();131132String name = inFile.getPath();133134FileUtils.listFileDescriptors(System.out);135long fdCount0 = getFdCount();136137int failCount = 0;138failCount += test(new FileOutputStream(name));139140failCount += test(new StreamOverrides(name));141142failCount += test(new StreamOverridesClose(name));143144failCount += test(new StreamOverridesFinalize(name));145146failCount += test(new StreamOverridesFinalizeClose(name));147148if (failCount > 0) {149throw new AssertionError("Failed test count: " + failCount);150}151152// Check the final count of open file descriptors153long fdCount = getFdCount();154if (fdCount != fdCount0) {155System.out.printf("initial count of open file descriptors: %d%n", fdCount0);156System.out.printf("final count of open file descriptors: %d%n", fdCount);157FileUtils.listFileDescriptors(System.out);158}159}160161// Get the count of open file descriptors, or -1 if not available162private static long getFdCount() {163OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();164return (mxBean instanceof UnixOperatingSystemMXBean)165? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()166: -1L;167}168169private static int test(FileOutputStream fos) throws Exception {170171try {172System.out.printf("%nTesting %s%n", fos.getClass().getName());173174// Prepare to wait for FOS to be reclaimed175ReferenceQueue<Object> queue = new ReferenceQueue<>();176HashSet<Reference<?>> pending = new HashSet<>();177WeakReference<FileOutputStream> msWeak = new WeakReference<>(fos, queue);178pending.add(msWeak);179180FileDescriptor fd = fos.getFD();181WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue);182pending.add(fdWeak);183184Field fdField = FileDescriptor.class.getDeclaredField("fd");185fdField.setAccessible(true);186int ffd = fdField.getInt(fd);187188Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");189cleanupField.setAccessible(true);190Object cleanup = cleanupField.get(fd);191System.out.printf(" cleanup: %s, ffd: %d, cf: %s%n", cleanup, ffd, cleanupField);192if (cleanup == null) {193throw new RuntimeException("cleanup should not be null");194}195196WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue);197pending.add(cleanupWeak);198System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n",199fdWeak, msWeak, cleanupWeak);200201AtomicInteger closeCounter = fos instanceof StreamOverrides202? ((StreamOverrides) fos).closeCounter() : null;203204Reference<?> r;205while (((r = queue.remove(1000L)) != null)206|| !pending.isEmpty()) {207System.out.printf(" r: %s, pending: %d%n",208r, pending.size());209if (r != null) {210pending.remove(r);211} else {212fos = null;213fd = null;214cleanup = null;215System.gc(); // attempt to reclaim them216}217}218Reference.reachabilityFence(fd);219Reference.reachabilityFence(fos);220Reference.reachabilityFence(cleanup);221222// Confirm the correct number of calls to close depending on the cleanup type223if (closeCounter != null && closeCounter.get() > 0) {224throw new RuntimeException("Close should not have been called: count: " + closeCounter);225}226} catch (Exception ex) {227ex.printStackTrace(System.out);228return 1;229}230return 0;231}232}233234235