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 }
|