Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/java/net/MulticastSocket/UnreferencedMulticastSockets.java
41149 views
1
/*
2
* Copyright (c) 2018, 2020, 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
* @library /test/lib
27
* @modules java.management java.base/java.io:+open java.base/java.net:+open
28
* java.base/sun.net java.base/sun.nio.ch:+open
29
* @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedMulticastSockets
30
* @run main/othervm -Djdk.net.usePlainDatagramSocketImpl UnreferencedMulticastSockets
31
* @run main/othervm UnreferencedMulticastSockets
32
* @summary Check that unreferenced multicast sockets are closed
33
*/
34
35
import java.io.FileDescriptor;
36
import java.lang.management.ManagementFactory;
37
import java.lang.management.OperatingSystemMXBean;
38
import java.lang.ref.ReferenceQueue;
39
import java.lang.ref.WeakReference;
40
import java.lang.reflect.Field;
41
import java.io.IOException;
42
import java.lang.reflect.Method;
43
import java.net.DatagramPacket;
44
import java.net.DatagramSocket;
45
import java.net.DatagramSocketImpl;
46
import java.net.InetAddress;
47
import java.net.InetSocketAddress;
48
import java.net.MulticastSocket;
49
import java.net.UnknownHostException;
50
import java.nio.channels.DatagramChannel;
51
import java.nio.file.Files;
52
import java.nio.file.Path;
53
import java.nio.file.Paths;
54
import java.security.AccessController;
55
import java.security.PrivilegedAction;
56
import java.util.ArrayDeque;
57
import java.util.List;
58
import java.util.Optional;
59
import java.util.concurrent.Phaser;
60
import java.util.concurrent.TimeUnit;
61
62
import jdk.test.lib.net.IPSupport;
63
64
import com.sun.management.UnixOperatingSystemMXBean;
65
import sun.net.NetProperties;
66
67
public class UnreferencedMulticastSockets {
68
69
/**
70
* The set of sockets we have to check up on.
71
*/
72
final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(5);
73
74
/**
75
* Queued objects when they are unreferenced.
76
*/
77
final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();
78
79
// Server to echo a datagram packet
80
static class Server implements Runnable {
81
82
MulticastSocket ss;
83
final int port;
84
final Phaser phaser = new Phaser(2);
85
Server() throws IOException {
86
InetAddress loopback = InetAddress.getLoopbackAddress();
87
InetSocketAddress serverAddress =
88
new InetSocketAddress(loopback, 0);
89
ss = new MulticastSocket(serverAddress);
90
port = ss.getLocalPort();
91
System.out.printf(" DatagramServer addr: %s: %d%n",
92
this.getHost(), this.getPort());
93
pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverMulticastSocket"));
94
extractRefs(ss, "serverMulticastSocket");
95
}
96
97
InetAddress getHost() throws UnknownHostException {
98
InetAddress localhost = InetAddress.getLoopbackAddress();
99
return localhost;
100
}
101
102
int getPort() {
103
return port;
104
}
105
106
// Receive a byte and send back a byte
107
public void run() {
108
try {
109
byte[] buffer = new byte[50];
110
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
111
ss.receive(p);
112
System.out.printf("Server: ping received from: %s%n", p.getSocketAddress());
113
phaser.arriveAndAwaitAdvance(); // await the client...
114
buffer[0] += 1;
115
System.out.printf("Server: sending echo to: %s%n", p.getSocketAddress());
116
ss.send(p); // send back +1
117
118
System.out.printf("Server: awaiting client%n");
119
phaser.arriveAndAwaitAdvance(); // await the client...
120
// do NOT close but 'forget' the socket reference
121
System.out.printf("Server: forgetting socket...%n");
122
ss = null;
123
} catch (Throwable ioe) {
124
ioe.printStackTrace();
125
}
126
}
127
}
128
129
public static void main(String args[]) throws Exception {
130
IPSupport.throwSkippedExceptionIfNonOperational();
131
132
InetSocketAddress clientAddress =
133
new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
134
135
// Create and close a MulticastSocket to warm up the FD count for side effects.
136
try (MulticastSocket s = new MulticastSocket(clientAddress)) {
137
// no-op; close immediately
138
s.getLocalPort(); // no-op
139
}
140
141
long fdCount0 = getFdCount();
142
listProcFD();
143
144
// start a server
145
Server svr = new Server();
146
Thread thr = new Thread(svr);
147
thr.start();
148
149
// It is possible under some circumstances that the client
150
// might get bound to the same port than the server: this
151
// would make the test fail - so if this happen we try to
152
// bind to a specific port by incrementing the server port.
153
MulticastSocket client = null;
154
int serverPort = svr.getPort();
155
int maxtries = 20;
156
for (int i = 0; i < maxtries; i++) {
157
try {
158
System.out.printf("Trying to bind client to: %s%n", clientAddress);
159
client = new MulticastSocket(clientAddress);
160
if (client.getLocalPort() != svr.getPort()) break;
161
client.close();
162
} catch (IOException x) {
163
System.out.printf("Couldn't create client after %d attempts: %s%n", i, x);
164
if (i == maxtries) throw x;
165
}
166
if (i == maxtries) {
167
String msg = String.format("Couldn't create client after %d attempts", i);
168
System.out.println(msg);
169
throw new AssertionError(msg);
170
}
171
clientAddress = new InetSocketAddress(clientAddress.getAddress(), serverPort + i);
172
}
173
174
System.out.printf(" client bound port: %s:%d%n",
175
client.getLocalAddress(), client.getLocalPort());
176
client.connect(svr.getHost(), svr.getPort());
177
pendingSockets.add(new NamedWeak(client, pendingQueue, "clientMulticastSocket"));
178
extractRefs(client, "clientMulticastSocket");
179
180
byte[] msg = new byte[1];
181
msg[0] = 1;
182
DatagramPacket p = new DatagramPacket(msg, msg.length, svr.getHost(), svr.getPort());
183
client.send(p);
184
System.out.printf(" ping sent to: %s:%d%n", svr.getHost(), svr.getPort());
185
svr.phaser.arriveAndAwaitAdvance(); // wait until the server has received its packet
186
187
p = new DatagramPacket(msg, msg.length);
188
client.receive(p);
189
190
System.out.printf(" echo received from: %s%n", p.getSocketAddress());
191
if (msg[0] != 2) {
192
throw new AssertionError("incorrect data received: expected: 2, actual: " + msg[0]);
193
}
194
svr.phaser.arriveAndAwaitAdvance(); // let the server null out its socket
195
196
// Do NOT close the MulticastSocket; forget it
197
198
Object ref;
199
int loops = 20;
200
while (!pendingSockets.isEmpty() && loops-- > 0) {
201
ref = pendingQueue.remove(1000L);
202
if (ref != null) {
203
pendingSockets.remove(ref);
204
System.out.printf(" ref freed: %s, remaining: %d%n", ref, pendingSockets.size());
205
} else {
206
client = null;
207
p = null;
208
msg = null;
209
System.gc();
210
}
211
}
212
213
thr.join();
214
215
// List the open file descriptors
216
long fdCount = getFdCount();
217
System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);
218
listProcFD();
219
220
if (loops == 0) {
221
throw new AssertionError("Not all references reclaimed");
222
}
223
}
224
225
// Get the count of open file descriptors, or -1 if not available
226
private static long getFdCount() {
227
OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
228
return (mxBean instanceof UnixOperatingSystemMXBean)
229
? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
230
: -1L;
231
}
232
233
private static boolean usePlainDatagramSocketImpl() {
234
PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainDatagramSocketImpl");
235
String s = AccessController.doPrivileged(pa);
236
return (s != null) && (s.isEmpty() || s.equalsIgnoreCase("true"));
237
}
238
239
// Reflect to find references in the datagram implementation that will be gc'd
240
private static void extractRefs(DatagramSocket s, String name) {
241
242
try {
243
Field datagramSocketField = DatagramSocket.class.getDeclaredField("delegate");
244
datagramSocketField.setAccessible(true);
245
246
if (!usePlainDatagramSocketImpl()) {
247
// MulticastSocket using DatagramSocketAdaptor
248
Object MulticastSocket = datagramSocketField.get(s);
249
250
Method m = DatagramSocket.class.getDeclaredMethod("getChannel");
251
m.setAccessible(true);
252
DatagramChannel datagramChannel = (DatagramChannel) m.invoke(MulticastSocket);
253
254
assert datagramChannel.getClass() == Class.forName("sun.nio.ch.DatagramChannelImpl");
255
256
Field fileDescriptorField = datagramChannel.getClass().getDeclaredField("fd");
257
fileDescriptorField.setAccessible(true);
258
FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(datagramChannel);
259
extractRefs(fileDescriptor, name);
260
261
} else {
262
// MulticastSocket using PlainDatagramSocketImpl
263
Object MulticastSocket = datagramSocketField.get(s);
264
assert MulticastSocket.getClass() == Class.forName("java.net.NetMulticastSocket");
265
266
Method m = MulticastSocket.getClass().getDeclaredMethod("getImpl");
267
m.setAccessible(true);
268
DatagramSocketImpl datagramSocketImpl = (DatagramSocketImpl) m.invoke(MulticastSocket);
269
270
Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
271
fileDescriptorField.setAccessible(true);
272
FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(datagramSocketImpl);
273
extractRefs(fileDescriptor, name);
274
275
Class<?> socketImplClass = datagramSocketImpl.getClass();
276
System.out.printf("socketImplClass: %s%n", socketImplClass);
277
if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
278
Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
279
fileDescriptor1Field.setAccessible(true);
280
FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(datagramSocketImpl);
281
extractRefs(fileDescriptor1, name + "::twoStacksFd1");
282
283
} else {
284
System.out.printf("socketImpl class name not matched: %s != %s%n",
285
socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
286
}
287
}
288
} catch (Exception ex) {
289
ex.printStackTrace();
290
throw new AssertionError("missing field", ex);
291
}
292
}
293
294
private static void extractRefs(FileDescriptor fileDescriptor, String name) {
295
Object cleanup = null;
296
int rawfd = -1;
297
try {
298
if (fileDescriptor != null) {
299
Field fd1Field = FileDescriptor.class.getDeclaredField("fd");
300
fd1Field.setAccessible(true);
301
rawfd = fd1Field.getInt(fileDescriptor);
302
303
Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");
304
cleanupfdField.setAccessible(true);
305
cleanup = cleanupfdField.get(fileDescriptor);
306
pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
307
name + "::fileDescriptor: " + rawfd));
308
pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
309
310
}
311
} catch (NoSuchFieldException | IllegalAccessException ex) {
312
ex.printStackTrace();
313
throw new AssertionError("missing field", ex);
314
} finally {
315
System.out.print(String.format(" %s:: fd: %s, fd: %d, cleanup: %s%n",
316
name, fileDescriptor, rawfd, cleanup));
317
}
318
}
319
320
/**
321
* Method to list the open file descriptors (if supported by the 'lsof' command).
322
*/
323
static void listProcFD() {
324
List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");
325
Optional<Path> lsof = lsofDirs.stream()
326
.map(s -> Paths.get(s, "lsof"))
327
.filter(f -> Files.isExecutable(f))
328
.findFirst();
329
lsof.ifPresent(exe -> {
330
try {
331
System.out.printf("Open File Descriptors:%n");
332
long pid = ProcessHandle.current().pid();
333
ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));
334
pb.inheritIO();
335
Process p = pb.start();
336
p.waitFor(10, TimeUnit.SECONDS);
337
} catch (IOException | InterruptedException ie) {
338
ie.printStackTrace();
339
}
340
});
341
}
342
343
344
// Simple class to identify which refs have been queued
345
static class NamedWeak extends WeakReference<Object> {
346
private final String name;
347
348
NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {
349
super(o, queue);
350
this.name = name;
351
}
352
353
public String toString() {
354
return name + "; " + super.toString();
355
}
356
}
357
}
358
359