mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8195059: Update java.net Socket and DatagramSocket implementations to use Cleaner
Reviewed-by: chegar, plevart
This commit is contained in:
parent
89c2e03b1d
commit
0b8689b331
15 changed files with 1113 additions and 60 deletions
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
# Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
#
|
#
|
||||||
# This code is free software; you can redistribute it and/or modify it
|
# This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -89,6 +89,7 @@ SUNWprivate_1.1 {
|
||||||
Java_java_net_PlainDatagramSocketImpl_setTimeToLive;
|
Java_java_net_PlainDatagramSocketImpl_setTimeToLive;
|
||||||
Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0;
|
Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0;
|
||||||
Java_java_net_AbstractPlainDatagramSocketImpl_isReusePortAvailable0;
|
Java_java_net_AbstractPlainDatagramSocketImpl_isReusePortAvailable0;
|
||||||
|
Java_java_net_SocketCleanable_cleanupClose0;
|
||||||
Java_jdk_net_Sockets_isReusePortAvailable0;
|
Java_jdk_net_Sockets_isReusePortAvailable0;
|
||||||
Java_sun_net_PortConfig_getUpper0;
|
Java_sun_net_PortConfig_getUpper0;
|
||||||
Java_sun_net_PortConfig_getLower0;
|
Java_sun_net_PortConfig_getLower0;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,11 +26,11 @@ package java.net;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.AccessController;
|
|
||||||
import sun.net.ResourceManager;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import sun.net.ResourceManager;
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,6 +115,7 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
||||||
fd = new FileDescriptor();
|
fd = new FileDescriptor();
|
||||||
try {
|
try {
|
||||||
datagramSocketCreate();
|
datagramSocketCreate();
|
||||||
|
SocketCleanable.register(fd);
|
||||||
} catch (SocketException ioe) {
|
} catch (SocketException ioe) {
|
||||||
ResourceManager.afterUdpClose();
|
ResourceManager.afterUdpClose();
|
||||||
fd = null;
|
fd = null;
|
||||||
|
@ -265,6 +266,7 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
||||||
*/
|
*/
|
||||||
protected void close() {
|
protected void close() {
|
||||||
if (fd != null) {
|
if (fd != null) {
|
||||||
|
SocketCleanable.unregister(fd);
|
||||||
datagramSocketClose();
|
datagramSocketClose();
|
||||||
ResourceManager.afterUdpClose();
|
ResourceManager.afterUdpClose();
|
||||||
fd = null;
|
fd = null;
|
||||||
|
@ -275,11 +277,6 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
||||||
return (fd == null) ? true : false;
|
return (fd == null) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected void finalize() {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set a value - since we only support (setting) binary options
|
* set a value - since we only support (setting) binary options
|
||||||
* here, o must be a Boolean
|
* here, o must be a Boolean
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,17 +25,18 @@
|
||||||
|
|
||||||
package java.net;
|
package java.net;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.FileDescriptor;
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import sun.net.ConnectionResetException;
|
import sun.net.ConnectionResetException;
|
||||||
import sun.net.NetHooks;
|
import sun.net.NetHooks;
|
||||||
import sun.net.ResourceManager;
|
import sun.net.ResourceManager;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Socket Implementation. This implementation does
|
* Default Socket Implementation. This implementation does
|
||||||
|
@ -136,6 +137,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl
|
||||||
fd = new FileDescriptor();
|
fd = new FileDescriptor();
|
||||||
try {
|
try {
|
||||||
socketCreate(false);
|
socketCreate(false);
|
||||||
|
SocketCleanable.register(fd);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
ResourceManager.afterUdpClose();
|
ResourceManager.afterUdpClose();
|
||||||
fd = null;
|
fd = null;
|
||||||
|
@ -144,6 +146,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl
|
||||||
} else {
|
} else {
|
||||||
fd = new FileDescriptor();
|
fd = new FileDescriptor();
|
||||||
socketCreate(true);
|
socketCreate(true);
|
||||||
|
SocketCleanable.register(fd);
|
||||||
}
|
}
|
||||||
if (socket != null)
|
if (socket != null)
|
||||||
socket.setCreated();
|
socket.setCreated();
|
||||||
|
@ -643,14 +646,6 @@ abstract class AbstractPlainSocketImpl extends SocketImpl
|
||||||
socketSendUrgentData (data);
|
socketSendUrgentData (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans up if the user forgets to close it.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected void finalize() throws IOException {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Acquires" and returns the FileDescriptor for this impl
|
* "Acquires" and returns the FileDescriptor for this impl
|
||||||
*
|
*
|
||||||
|
@ -748,6 +743,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl
|
||||||
* Close the socket (and release the file descriptor).
|
* Close the socket (and release the file descriptor).
|
||||||
*/
|
*/
|
||||||
protected void socketClose() throws IOException {
|
protected void socketClose() throws IOException {
|
||||||
|
SocketCleanable.unregister(fd);
|
||||||
socketClose0(false);
|
socketClose0(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -549,6 +549,7 @@ class ServerSocket implements java.io.Closeable {
|
||||||
si.address = new InetAddress();
|
si.address = new InetAddress();
|
||||||
si.fd = new FileDescriptor();
|
si.fd = new FileDescriptor();
|
||||||
getImpl().accept(si);
|
getImpl().accept(si);
|
||||||
|
SocketCleanable.register(si.fd); // raw fd has been set
|
||||||
|
|
||||||
SecurityManager security = System.getSecurityManager();
|
SecurityManager security = System.getSecurityManager();
|
||||||
if (security != null) {
|
if (security != null) {
|
||||||
|
|
107
src/java.base/share/classes/java/net/SocketCleanable.java
Normal file
107
src/java.base/share/classes/java/net/SocketCleanable.java
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package java.net;
|
||||||
|
|
||||||
|
import jdk.internal.misc.JavaIOFileDescriptorAccess;
|
||||||
|
import jdk.internal.misc.SharedSecrets;
|
||||||
|
import jdk.internal.ref.CleanerFactory;
|
||||||
|
import jdk.internal.ref.PhantomCleanable;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup for a socket/datagramsocket FileDescriptor when it becomes phantom reachable.
|
||||||
|
* Create a cleanup if the raw fd != -1. Windows closes sockets using the fd.
|
||||||
|
* Subclassed from {@code PhantomCleanable} so that {@code clear} can be
|
||||||
|
* called to disable the cleanup when the socket fd is closed by any means
|
||||||
|
* other than calling {@link FileDescriptor#close}.
|
||||||
|
* Otherwise, it would incorrectly close the handle or fd after it has been reused.
|
||||||
|
*/
|
||||||
|
final class SocketCleanable extends PhantomCleanable<Object> {
|
||||||
|
|
||||||
|
// Access to FileDescriptor internals
|
||||||
|
private static final JavaIOFileDescriptorAccess fdAccess =
|
||||||
|
SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||||
|
|
||||||
|
// Native function to call NET_SocketClose(fd)
|
||||||
|
private static native void cleanupClose0(int fd) throws IOException;
|
||||||
|
|
||||||
|
// The raw fd to close
|
||||||
|
private final int fd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a socket specific Cleanable with the FileDescriptor
|
||||||
|
* if the FileDescriptor is non-null and the raw fd is != -1.
|
||||||
|
*
|
||||||
|
* @param fdo the FileDescriptor; may be null
|
||||||
|
*/
|
||||||
|
static void register(FileDescriptor fdo) {
|
||||||
|
if (fdo != null) {
|
||||||
|
int fd = fdAccess.get(fdo);
|
||||||
|
if (fd != -1) {
|
||||||
|
fdAccess.registerCleanup(fdo,
|
||||||
|
new SocketCleanable(fdo, CleanerFactory.cleaner(), fd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a Cleanable from the FileDescriptor.
|
||||||
|
* @param fdo the FileDescriptor; may be null
|
||||||
|
*/
|
||||||
|
static void unregister(FileDescriptor fdo) {
|
||||||
|
if (fdo != null) {
|
||||||
|
fdAccess.unregisterCleanup(fdo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a phantom cleanable reference.
|
||||||
|
*
|
||||||
|
* @param obj the object to monitor
|
||||||
|
* @param cleaner the cleaner
|
||||||
|
* @param fd file descriptor to close
|
||||||
|
*/
|
||||||
|
private SocketCleanable(Object obj, Cleaner cleaner, int fd) {
|
||||||
|
super(obj, cleaner);
|
||||||
|
this.fd = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the native handle or fd.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void performCleanup() {
|
||||||
|
try {
|
||||||
|
cleanupClose0(fd);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new UncheckedIOException("close", ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -27,6 +27,8 @@ package jdk.internal.misc;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import jdk.internal.ref.PhantomCleanable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @author Chris Hegarty
|
* @author Chris Hegarty
|
||||||
*/
|
*/
|
||||||
|
@ -38,6 +40,8 @@ public interface JavaIOFileDescriptorAccess {
|
||||||
public boolean getAppend(FileDescriptor fdo);
|
public boolean getAppend(FileDescriptor fdo);
|
||||||
public void close(FileDescriptor fdo) throws IOException;
|
public void close(FileDescriptor fdo) throws IOException;
|
||||||
public void registerCleanup(FileDescriptor fdo);
|
public void registerCleanup(FileDescriptor fdo);
|
||||||
|
public void registerCleanup(FileDescriptor fdo, PhantomCleanable<Object> cleanable);
|
||||||
|
public void unregisterCleanup(FileDescriptor fdo);
|
||||||
|
|
||||||
// Only valid on Windows
|
// Only valid on Windows
|
||||||
public void setHandle(FileDescriptor fdo, long handle);
|
public void setHandle(FileDescriptor fdo, long handle);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -91,6 +91,14 @@ public final class FileDescriptor {
|
||||||
fdo.registerCleanup();
|
fdo.registerCleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerCleanup(FileDescriptor fdo, PhantomCleanable<Object> cleanup) {
|
||||||
|
fdo.registerCleanup(cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterCleanup(FileDescriptor fdo) {
|
||||||
|
fdo.unregisterCleanup();
|
||||||
|
}
|
||||||
|
|
||||||
public void setHandle(FileDescriptor fdo, long handle) {
|
public void setHandle(FileDescriptor fdo, long handle) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
@ -105,7 +113,7 @@ public final class FileDescriptor {
|
||||||
/**
|
/**
|
||||||
* Cleanup in case FileDescriptor is not explicitly closed.
|
* Cleanup in case FileDescriptor is not explicitly closed.
|
||||||
*/
|
*/
|
||||||
private FDCleanup cleanup;
|
private PhantomCleanable<Object> cleanup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an (invalid) FileDescriptor
|
* Constructs an (invalid) FileDescriptor
|
||||||
|
@ -206,16 +214,44 @@ public final class FileDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a cleanup for the current raw fd.
|
* Register a cleanup for the current handle.
|
||||||
* Used directly in java.io and indirectly via fdAccess.
|
* Used directly in java.io and indirectly via fdAccess.
|
||||||
* The cleanup should be registered after the fd is set in the FileDescriptor.
|
* The cleanup should be registered after the handle is set in the FileDescriptor.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
synchronized void registerCleanup() {
|
void registerCleanup() {
|
||||||
|
registerCleanup(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a cleanup for the current handle.
|
||||||
|
* Used directly in java.io and indirectly via fdAccess.
|
||||||
|
* The cleanup should be registered after the handle is set in the FileDescriptor.
|
||||||
|
* @param newCleanable a PhantomCleanable to register
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
synchronized void registerCleanup(PhantomCleanable<Object> newCleanable) {
|
||||||
if (cleanup != null) {
|
if (cleanup != null) {
|
||||||
cleanup.clear();
|
cleanup.clear();
|
||||||
}
|
}
|
||||||
cleanup = FDCleanup.create(this);
|
cleanup = (newCleanable == null) ? FDCleanup.create(this) : newCleanable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a cleanup for the current raw fd.
|
||||||
|
* Used directly in java.io and indirectly via fdAccess.
|
||||||
|
* Normally {@link #close()} should be used except in cases where
|
||||||
|
* it is certain the caller will close the raw fd and the cleanup
|
||||||
|
* must not close the raw fd. {@link #unregisterCleanup()} must be
|
||||||
|
* called before the raw fd is closed to prevent a race that makes
|
||||||
|
* it possible for the fd to be reallocated to another use and later
|
||||||
|
* the cleanup might be invoked.
|
||||||
|
*/
|
||||||
|
synchronized void unregisterCleanup() {
|
||||||
|
if (cleanup != null) {
|
||||||
|
cleanup.clear();
|
||||||
|
}
|
||||||
|
cleanup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -27,6 +27,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "net_util.h"
|
#include "net_util.h"
|
||||||
|
#include "java_net_SocketCleanable.h"
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
|
Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
|
||||||
|
@ -45,3 +46,15 @@ Java_jdk_net_Sockets_isReusePortAvailable0(JNIEnv* env, jclass c1)
|
||||||
{
|
{
|
||||||
return (reuseport_available()) ? JNI_TRUE : JNI_FALSE;
|
return (reuseport_available()) ? JNI_TRUE : JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: java_net_SocketCleanable
|
||||||
|
* Method: cleanupClose0
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_java_net_SocketCleanable_cleanupClose0(JNIEnv *env, jclass c1, jint fd)
|
||||||
|
{
|
||||||
|
NET_SocketClose(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -90,7 +90,15 @@ public final class FileDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerCleanup(FileDescriptor fdo) {
|
public void registerCleanup(FileDescriptor fdo) {
|
||||||
fdo.registerCleanup();
|
fdo.registerCleanup(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerCleanup(FileDescriptor fdo, PhantomCleanable<Object> cleanup) {
|
||||||
|
fdo.registerCleanup(cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterCleanup(FileDescriptor fdo) {
|
||||||
|
fdo.unregisterCleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHandle(FileDescriptor fdo, long handle) {
|
public void setHandle(FileDescriptor fdo, long handle) {
|
||||||
|
@ -107,7 +115,7 @@ public final class FileDescriptor {
|
||||||
/**
|
/**
|
||||||
* Cleanup in case FileDescriptor is not explicitly closed.
|
* Cleanup in case FileDescriptor is not explicitly closed.
|
||||||
*/
|
*/
|
||||||
private FDCleanup cleanup;
|
private PhantomCleanable<Object> cleanup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an (invalid) FileDescriptor
|
* Constructs an (invalid) FileDescriptor
|
||||||
|
@ -217,11 +225,39 @@ public final class FileDescriptor {
|
||||||
* The cleanup should be registered after the handle is set in the FileDescriptor.
|
* The cleanup should be registered after the handle is set in the FileDescriptor.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
synchronized void registerCleanup() {
|
void registerCleanup() {
|
||||||
|
registerCleanup(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a cleanup for the current handle.
|
||||||
|
* Used directly in java.io and indirectly via fdAccess.
|
||||||
|
* The cleanup should be registered after the handle is set in the FileDescriptor.
|
||||||
|
* @param newCleanable a PhantomCleanable to register
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
synchronized void registerCleanup(PhantomCleanable<Object> newCleanable) {
|
||||||
if (cleanup != null) {
|
if (cleanup != null) {
|
||||||
cleanup.clear();
|
cleanup.clear();
|
||||||
}
|
}
|
||||||
cleanup = FDCleanup.create(this);
|
cleanup = (newCleanable == null) ? FDCleanup.create(this) : newCleanable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a cleanup for the current raw fd.
|
||||||
|
* Used directly in java.io and indirectly via fdAccess.
|
||||||
|
* Normally {@link #close()} should be used except in cases where
|
||||||
|
* it is certain the caller will close the raw fd and the cleanup
|
||||||
|
* must not close the raw fd. {@link #unregisterCleanup()} must be
|
||||||
|
* called before the raw fd is closed to prevent a race that makes
|
||||||
|
* it possible for the fd to be reallocated to another use and later
|
||||||
|
* the cleanup might be invoked.
|
||||||
|
*/
|
||||||
|
synchronized void unregisterCleanup() {
|
||||||
|
if (cleanup != null) {
|
||||||
|
cleanup.clear();
|
||||||
|
}
|
||||||
|
cleanup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -319,16 +355,21 @@ public final class FileDescriptor {
|
||||||
/**
|
/**
|
||||||
* Cleanup for a FileDescriptor when it becomes phantom reachable.
|
* Cleanup for a FileDescriptor when it becomes phantom reachable.
|
||||||
* Create a cleanup if handle != -1.
|
* Create a cleanup if handle != -1.
|
||||||
|
* Windows closes files using handles and sockets via the fd.
|
||||||
|
* Network FileDescriptors using socket fd must provide their
|
||||||
|
* own PhantomCleanable to {@link #registerCleanup}.
|
||||||
|
* This implementation only clears thehandles.
|
||||||
|
* <p>
|
||||||
* Subclassed from {@code PhantomCleanable} so that {@code clear} can be
|
* Subclassed from {@code PhantomCleanable} so that {@code clear} can be
|
||||||
* called to disable the cleanup when the fd is closed by any means other
|
* called to disable the cleanup when the handle is closed by any means other
|
||||||
* than calling {@link FileDescriptor#close}.
|
* than calling {@link FileDescriptor#close}.
|
||||||
* Otherwise, it may close the handle after it has been reused.
|
* Otherwise, it may incorrectly close the handle after it has been reused.
|
||||||
*/
|
*/
|
||||||
static final class FDCleanup extends PhantomCleanable<Object> {
|
static final class FDCleanup extends PhantomCleanable<Object> {
|
||||||
private final long handle;
|
private final long handle;
|
||||||
|
|
||||||
static FDCleanup create(FileDescriptor fdo) {
|
static FDCleanup create(FileDescriptor fdo) {
|
||||||
return fdo.handle == -1
|
return fdo.handle == -1L
|
||||||
? null
|
? null
|
||||||
: new FDCleanup(fdo, CleanerFactory.cleaner(), fdo.handle);
|
: new FDCleanup(fdo, CleanerFactory.cleaner(), fdo.handle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,7 @@ class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
|
||||||
fd1 = new FileDescriptor();
|
fd1 = new FileDescriptor();
|
||||||
try {
|
try {
|
||||||
super.create();
|
super.create();
|
||||||
|
SocketCleanable.register(fd1);
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
fd1 = null;
|
fd1 = null;
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -160,6 +161,8 @@ class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
|
||||||
|
|
||||||
protected void close() {
|
protected void close() {
|
||||||
if (fd != null || fd1 != null) {
|
if (fd != null || fd1 != null) {
|
||||||
|
SocketCleanable.unregister(fd);
|
||||||
|
SocketCleanable.unregister(fd1);
|
||||||
datagramSocketClose();
|
datagramSocketClose();
|
||||||
ResourceManager.afterUdpClose();
|
ResourceManager.afterUdpClose();
|
||||||
fd = null;
|
fd = null;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -24,6 +24,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#include "net_util.h"
|
||||||
|
#include "java_net_SocketCleanable.h"
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
|
Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
|
||||||
|
@ -45,3 +47,15 @@ Java_jdk_net_Sockets_isReusePortAvailable0(JNIEnv* env, jclass c1)
|
||||||
// SO_REUSEPORT is not supported on Windows
|
// SO_REUSEPORT is not supported on Windows
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: java_net_SocketCleanable
|
||||||
|
* Method: cleanupClose0
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_java_net_SocketCleanable_cleanupClose0(JNIEnv *env, jclass c1, jint fd)
|
||||||
|
{
|
||||||
|
NET_SocketClose(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
/* @test
|
/* @test
|
||||||
*
|
*
|
||||||
* @bug 4095393
|
* @bug 4095393
|
||||||
*
|
* @test main SendSize
|
||||||
* @summary this tests a regression in 1.2, beta 2 and earlier where
|
* @summary this tests a regression in 1.2, beta 2 and earlier where
|
||||||
* the DatagramPackets sent the entire buffer, not the buffer length
|
* the DatagramPackets sent the entire buffer, not the buffer length
|
||||||
* of the packet.
|
* of the packet.
|
||||||
|
@ -43,8 +43,12 @@ public class SendSize {
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
DatagramSocket serverSocket = new DatagramSocket();
|
DatagramSocket serverSocket = new DatagramSocket();
|
||||||
new ServerThread(serverSocket).start();
|
Thread server = new ServerThread(serverSocket);
|
||||||
new ClientThread(serverSocket.getLocalPort()).start();
|
server.start();
|
||||||
|
Thread client = new ClientThread(serverSocket.getLocalPort());
|
||||||
|
client.start();
|
||||||
|
server.join();
|
||||||
|
client.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ServerThread extends Thread {
|
static class ServerThread extends Thread {
|
||||||
|
@ -58,24 +62,26 @@ public class SendSize {
|
||||||
try {
|
try {
|
||||||
System.err.println("started server thread: " + server);
|
System.err.println("started server thread: " + server);
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
DatagramPacket receivePacket = new DatagramPacket(buf,
|
for (int i = 0; i < 10; i++) {
|
||||||
buf.length);
|
DatagramPacket receivePacket = new DatagramPacket(buf,
|
||||||
server.receive(receivePacket);
|
buf.length);
|
||||||
int len = receivePacket.getLength();
|
server.receive(receivePacket);
|
||||||
switch(len) {
|
int len = receivePacket.getLength();
|
||||||
case packetLength:
|
switch (len) {
|
||||||
System.out.println("receive length is: " + len);
|
case packetLength:
|
||||||
break;
|
System.out.println("receive length is: " + len);
|
||||||
default:
|
break;
|
||||||
throw new RuntimeException(
|
default:
|
||||||
"receive length is: " + len +
|
throw new RuntimeException(
|
||||||
", send length: " + packetLength +
|
"receive length is: " + len +
|
||||||
", buffer length: " + buf.length);
|
", send length: " + packetLength +
|
||||||
|
", buffer length: " + buf.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException("caugth: " + e);
|
throw new RuntimeException("caught: " + e);
|
||||||
} finally {
|
} finally {
|
||||||
if (server != null) { server.close(); }
|
if (server != null) { server.close(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @modules java.management java.base/java.io:+open java.base/java.net:+open
|
||||||
|
* @run main/othervm UnreferencedDatagramSockets
|
||||||
|
* @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedDatagramSockets
|
||||||
|
* @summary Check that unreferenced datagram sockets are closed
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.management.OperatingSystemMXBean;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.DatagramSocketImpl;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.sun.management.UnixOperatingSystemMXBean;
|
||||||
|
|
||||||
|
public class UnreferencedDatagramSockets {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of sockets we have to check up on.
|
||||||
|
*/
|
||||||
|
final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queued objects when they are unreferenced.
|
||||||
|
*/
|
||||||
|
final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();
|
||||||
|
|
||||||
|
// Server to echo a datagram packet
|
||||||
|
static class Server implements Runnable {
|
||||||
|
|
||||||
|
DatagramSocket ss;
|
||||||
|
|
||||||
|
Server() throws IOException {
|
||||||
|
ss = new DatagramSocket(0);
|
||||||
|
System.out.printf(" DatagramServer addr: %s: %d%n",
|
||||||
|
this.getHost(), this.getPort());
|
||||||
|
pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverDatagramSocket"));
|
||||||
|
extractRefs(ss, "serverDatagramSocket");
|
||||||
|
}
|
||||||
|
|
||||||
|
InetAddress getHost() throws UnknownHostException {
|
||||||
|
InetAddress localhost = InetAddress.getByName("localhost"); //.getLocalHost();
|
||||||
|
return localhost;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPort() {
|
||||||
|
return ss.getLocalPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive a byte and send back a byte
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
byte[] buffer = new byte[50];
|
||||||
|
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
|
||||||
|
ss.receive(p);
|
||||||
|
buffer[0] += 1;
|
||||||
|
ss.send(p); // send back +1
|
||||||
|
|
||||||
|
// do NOT close but 'forget' the datagram socket reference
|
||||||
|
ss = null;
|
||||||
|
} catch (Exception ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
|
||||||
|
// Create and close a DatagramSocket to warm up the FD count for side effects.
|
||||||
|
try (DatagramSocket s = new DatagramSocket(0)) {
|
||||||
|
// no-op; close immediately
|
||||||
|
s.getLocalPort(); // no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
long fdCount0 = getFdCount();
|
||||||
|
listProcFD();
|
||||||
|
|
||||||
|
// start a server
|
||||||
|
Server svr = new Server();
|
||||||
|
Thread thr = new Thread(svr);
|
||||||
|
thr.start();
|
||||||
|
|
||||||
|
DatagramSocket client = new DatagramSocket(0);
|
||||||
|
client.connect(svr.getHost(), svr.getPort());
|
||||||
|
pendingSockets.add(new NamedWeak(client, pendingQueue, "clientDatagramSocket"));
|
||||||
|
extractRefs(client, "clientDatagramSocket");
|
||||||
|
|
||||||
|
byte[] msg = new byte[1];
|
||||||
|
msg[0] = 1;
|
||||||
|
DatagramPacket p = new DatagramPacket(msg, msg.length, svr.getHost(), svr.getPort());
|
||||||
|
client.send(p);
|
||||||
|
|
||||||
|
p = new DatagramPacket(msg, msg.length);
|
||||||
|
client.receive(p);
|
||||||
|
|
||||||
|
System.out.printf("echo received from: %s%n", p.getSocketAddress());
|
||||||
|
if (msg[0] != 2) {
|
||||||
|
throw new AssertionError("incorrect data received: expected: 2, actual: " + msg[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do NOT close the DatagramSocket; forget it
|
||||||
|
|
||||||
|
Object ref;
|
||||||
|
int loops = 20;
|
||||||
|
while (!pendingSockets.isEmpty() && loops-- > 0) {
|
||||||
|
ref = pendingQueue.remove(1000L);
|
||||||
|
if (ref != null) {
|
||||||
|
pendingSockets.remove(ref);
|
||||||
|
System.out.printf(" ref freed: %s, remaining: %d%n", ref, pendingSockets.size());
|
||||||
|
} else {
|
||||||
|
client = null;
|
||||||
|
p = null;
|
||||||
|
msg = null;
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thr.join();
|
||||||
|
|
||||||
|
// List the open file descriptors
|
||||||
|
long fdCount = getFdCount();
|
||||||
|
System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);
|
||||||
|
listProcFD();
|
||||||
|
|
||||||
|
if (loops == 0) {
|
||||||
|
throw new AssertionError("Not all references reclaimed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the count of open file descriptors, or -1 if not available
|
||||||
|
private static long getFdCount() {
|
||||||
|
OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
|
||||||
|
return (mxBean instanceof UnixOperatingSystemMXBean)
|
||||||
|
? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
|
||||||
|
: -1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflect to find references in the datagram implementation that will be gc'd
|
||||||
|
private static void extractRefs(DatagramSocket s, String name) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Field socketImplField = DatagramSocket.class.getDeclaredField("impl");
|
||||||
|
socketImplField.setAccessible(true);
|
||||||
|
Object socketImpl = socketImplField.get(s);
|
||||||
|
|
||||||
|
Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
|
||||||
|
fileDescriptorField.setAccessible(true);
|
||||||
|
FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
|
||||||
|
extractRefs(fileDescriptor, name);
|
||||||
|
|
||||||
|
Class<?> socketImplClass = socketImpl.getClass();
|
||||||
|
System.out.printf("socketImplClass: %s%n", socketImplClass);
|
||||||
|
if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
|
||||||
|
Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
|
||||||
|
fileDescriptor1Field.setAccessible(true);
|
||||||
|
FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
|
||||||
|
extractRefs(fileDescriptor1, name + "::twoStacksFd1");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
System.out.printf("socketImpl class name not matched: %s != %s%n",
|
||||||
|
socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new AssertionError("missing field", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void extractRefs(FileDescriptor fileDescriptor, String name) {
|
||||||
|
Object cleanup = null;
|
||||||
|
int rawfd = -1;
|
||||||
|
try {
|
||||||
|
if (fileDescriptor != null) {
|
||||||
|
Field fd1Field = FileDescriptor.class.getDeclaredField("fd");
|
||||||
|
fd1Field.setAccessible(true);
|
||||||
|
rawfd = fd1Field.getInt(fileDescriptor);
|
||||||
|
|
||||||
|
Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");
|
||||||
|
cleanupfdField.setAccessible(true);
|
||||||
|
cleanup = cleanupfdField.get(fileDescriptor);
|
||||||
|
pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
|
||||||
|
name + "::fileDescriptor: " + rawfd));
|
||||||
|
pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new AssertionError("missing field", ex);
|
||||||
|
} finally {
|
||||||
|
System.out.print(String.format(" %s:: fd: %s, fd: %d, cleanup: %s%n",
|
||||||
|
name, fileDescriptor, rawfd, cleanup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to list the open file descriptors (if supported by the 'lsof' command).
|
||||||
|
*/
|
||||||
|
static void listProcFD() {
|
||||||
|
List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");
|
||||||
|
Optional<Path> lsof = lsofDirs.stream()
|
||||||
|
.map(s -> Paths.get(s, "lsof"))
|
||||||
|
.filter(f -> Files.isExecutable(f))
|
||||||
|
.findFirst();
|
||||||
|
lsof.ifPresent(exe -> {
|
||||||
|
try {
|
||||||
|
System.out.printf("Open File Descriptors:%n");
|
||||||
|
long pid = ProcessHandle.current().pid();
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));
|
||||||
|
pb.inheritIO();
|
||||||
|
Process p = pb.start();
|
||||||
|
p.waitFor(10, TimeUnit.SECONDS);
|
||||||
|
} catch (IOException | InterruptedException ie) {
|
||||||
|
ie.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple class to identify which refs have been queued
|
||||||
|
static class NamedWeak extends WeakReference<Object> {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {
|
||||||
|
super(o, queue);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name + "; " + super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @modules java.management java.base/java.io:+open java.base/java.net:+open
|
||||||
|
* @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedMulticastSockets
|
||||||
|
* @run main/othervm UnreferencedMulticastSockets
|
||||||
|
* @summary Check that unreferenced multicast sockets are closed
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.management.OperatingSystemMXBean;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.DatagramSocketImpl;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.MulticastSocket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.sun.management.UnixOperatingSystemMXBean;
|
||||||
|
|
||||||
|
public class UnreferencedMulticastSockets {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of sockets we have to check up on.
|
||||||
|
*/
|
||||||
|
final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queued objects when they are unreferenced.
|
||||||
|
*/
|
||||||
|
final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();
|
||||||
|
|
||||||
|
// Server to echo a datagram packet
|
||||||
|
static class Server implements Runnable {
|
||||||
|
|
||||||
|
MulticastSocket ss;
|
||||||
|
|
||||||
|
Server() throws IOException {
|
||||||
|
ss = new MulticastSocket(0);
|
||||||
|
System.out.printf(" DatagramServer addr: %s: %d%n",
|
||||||
|
this.getHost(), this.getPort());
|
||||||
|
pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverMulticastSocket"));
|
||||||
|
extractRefs(ss, "serverMulticastSocket");
|
||||||
|
}
|
||||||
|
|
||||||
|
InetAddress getHost() throws UnknownHostException {
|
||||||
|
InetAddress localhost = InetAddress.getByName("localhost"); //.getLocalHost();
|
||||||
|
return localhost;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPort() {
|
||||||
|
return ss.getLocalPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive a byte and send back a byte
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
byte[] buffer = new byte[50];
|
||||||
|
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
|
||||||
|
ss.receive(p);
|
||||||
|
buffer[0] += 1;
|
||||||
|
ss.send(p); // send back +1
|
||||||
|
|
||||||
|
// do NOT close but 'forget' the socket reference
|
||||||
|
ss = null;
|
||||||
|
} catch (Exception ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
|
||||||
|
// Create and close a MulticastSocket to warm up the FD count for side effects.
|
||||||
|
try (MulticastSocket s = new MulticastSocket(0)) {
|
||||||
|
// no-op; close immediately
|
||||||
|
s.getLocalPort(); // no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
long fdCount0 = getFdCount();
|
||||||
|
listProcFD();
|
||||||
|
|
||||||
|
// start a server
|
||||||
|
Server svr = new Server();
|
||||||
|
Thread thr = new Thread(svr);
|
||||||
|
thr.start();
|
||||||
|
|
||||||
|
MulticastSocket client = new MulticastSocket(0);
|
||||||
|
client.connect(svr.getHost(), svr.getPort());
|
||||||
|
pendingSockets.add(new NamedWeak(client, pendingQueue, "clientMulticastSocket"));
|
||||||
|
extractRefs(client, "clientMulticastSocket");
|
||||||
|
|
||||||
|
byte[] msg = new byte[1];
|
||||||
|
msg[0] = 1;
|
||||||
|
DatagramPacket p = new DatagramPacket(msg, msg.length, svr.getHost(), svr.getPort());
|
||||||
|
client.send(p);
|
||||||
|
|
||||||
|
p = new DatagramPacket(msg, msg.length);
|
||||||
|
client.receive(p);
|
||||||
|
|
||||||
|
System.out.printf("echo received from: %s%n", p.getSocketAddress());
|
||||||
|
if (msg[0] != 2) {
|
||||||
|
throw new AssertionError("incorrect data received: expected: 2, actual: " + msg[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do NOT close the MulticastSocket; forget it
|
||||||
|
|
||||||
|
Object ref;
|
||||||
|
int loops = 20;
|
||||||
|
while (!pendingSockets.isEmpty() && loops-- > 0) {
|
||||||
|
ref = pendingQueue.remove(1000L);
|
||||||
|
if (ref != null) {
|
||||||
|
pendingSockets.remove(ref);
|
||||||
|
System.out.printf(" ref freed: %s, remaining: %d%n", ref, pendingSockets.size());
|
||||||
|
} else {
|
||||||
|
client = null;
|
||||||
|
p = null;
|
||||||
|
msg = null;
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thr.join();
|
||||||
|
|
||||||
|
// List the open file descriptors
|
||||||
|
long fdCount = getFdCount();
|
||||||
|
System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);
|
||||||
|
listProcFD();
|
||||||
|
|
||||||
|
if (loops == 0) {
|
||||||
|
throw new AssertionError("Not all references reclaimed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the count of open file descriptors, or -1 if not available
|
||||||
|
private static long getFdCount() {
|
||||||
|
OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
|
||||||
|
return (mxBean instanceof UnixOperatingSystemMXBean)
|
||||||
|
? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
|
||||||
|
: -1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflect to find references in the socket implementation that will be gc'd
|
||||||
|
private static void extractRefs(MulticastSocket s, String name) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Field socketImplField = DatagramSocket.class.getDeclaredField("impl");
|
||||||
|
socketImplField.setAccessible(true);
|
||||||
|
Object socketImpl = socketImplField.get(s);
|
||||||
|
|
||||||
|
Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
|
||||||
|
fileDescriptorField.setAccessible(true);
|
||||||
|
FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
|
||||||
|
extractRefs(fileDescriptor, name);
|
||||||
|
|
||||||
|
Class<?> socketImplClass = socketImpl.getClass();
|
||||||
|
System.out.printf("socketImplClass: %s%n", socketImplClass);
|
||||||
|
if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
|
||||||
|
Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
|
||||||
|
fileDescriptor1Field.setAccessible(true);
|
||||||
|
FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
|
||||||
|
extractRefs(fileDescriptor1, name + "::twoStacksFd1");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
System.out.printf("socketImpl class name not matched: %s != %s%n",
|
||||||
|
socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new AssertionError("missing field", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void extractRefs(FileDescriptor fileDescriptor, String name) {
|
||||||
|
Object cleanup = null;
|
||||||
|
int rawfd = -1;
|
||||||
|
try {
|
||||||
|
if (fileDescriptor != null) {
|
||||||
|
Field fd1Field = FileDescriptor.class.getDeclaredField("fd");
|
||||||
|
fd1Field.setAccessible(true);
|
||||||
|
rawfd = fd1Field.getInt(fileDescriptor);
|
||||||
|
|
||||||
|
Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");
|
||||||
|
cleanupfdField.setAccessible(true);
|
||||||
|
cleanup = cleanupfdField.get(fileDescriptor);
|
||||||
|
pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
|
||||||
|
name + "::fileDescriptor: " + rawfd));
|
||||||
|
pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new AssertionError("missing field", ex);
|
||||||
|
} finally {
|
||||||
|
System.out.print(String.format(" %s:: fd: %s, fd: %d, cleanup: %s%n",
|
||||||
|
name, fileDescriptor, rawfd, cleanup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to list the open file descriptors (if supported by the 'lsof' command).
|
||||||
|
*/
|
||||||
|
static void listProcFD() {
|
||||||
|
List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");
|
||||||
|
Optional<Path> lsof = lsofDirs.stream()
|
||||||
|
.map(s -> Paths.get(s, "lsof"))
|
||||||
|
.filter(f -> Files.isExecutable(f))
|
||||||
|
.findFirst();
|
||||||
|
lsof.ifPresent(exe -> {
|
||||||
|
try {
|
||||||
|
System.out.printf("Open File Descriptors:%n");
|
||||||
|
long pid = ProcessHandle.current().pid();
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));
|
||||||
|
pb.inheritIO();
|
||||||
|
Process p = pb.start();
|
||||||
|
p.waitFor(10, TimeUnit.SECONDS);
|
||||||
|
} catch (IOException | InterruptedException ie) {
|
||||||
|
ie.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Simple class to identify which refs have been queued
|
||||||
|
static class NamedWeak extends WeakReference<Object> {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {
|
||||||
|
super(o, queue);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name + "; " + super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
294
test/jdk/java/net/ServerSocket/UnreferencedSockets.java
Normal file
294
test/jdk/java/net/ServerSocket/UnreferencedSockets.java
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @modules java.management java.base/java.io:+open java.base/java.net:+open
|
||||||
|
* @run main/othervm UnreferencedSockets
|
||||||
|
* @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedSockets
|
||||||
|
* @summary Check that unreferenced sockets are closed
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.management.OperatingSystemMXBean;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketImpl;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.sun.management.UnixOperatingSystemMXBean;
|
||||||
|
|
||||||
|
public class UnreferencedSockets {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of sockets we have to check up on.
|
||||||
|
*/
|
||||||
|
final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(100);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queued sockets when they are unreferenced.
|
||||||
|
*/
|
||||||
|
final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();
|
||||||
|
|
||||||
|
// Server to echo a stream
|
||||||
|
static class Server implements Runnable {
|
||||||
|
|
||||||
|
ServerSocket ss;
|
||||||
|
|
||||||
|
Server() throws IOException {
|
||||||
|
ss = new ServerSocket(0);
|
||||||
|
pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverSocket"));
|
||||||
|
extractRefs(ss, "serverSocket");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int localPort() {
|
||||||
|
return ss.getLocalPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Socket s = ss.accept();
|
||||||
|
pendingSockets.add(new NamedWeak(s, pendingQueue, "acceptedSocket"));
|
||||||
|
extractRefs(s, "acceptedSocket");
|
||||||
|
|
||||||
|
InputStream in = s.getInputStream();
|
||||||
|
int b = in.read();
|
||||||
|
OutputStream out = s.getOutputStream();
|
||||||
|
out.write(b);
|
||||||
|
// do NOT close but 'forget' the socket reference
|
||||||
|
out = null;
|
||||||
|
in = null;
|
||||||
|
s = null;
|
||||||
|
} catch (Exception ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
ss.close();
|
||||||
|
ss = null;
|
||||||
|
} catch (IOException x) {
|
||||||
|
x.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
|
||||||
|
// Create and close a ServerSocket to warm up the FD count for side effects.
|
||||||
|
try (ServerSocket s = new ServerSocket(0)) {
|
||||||
|
// no-op; close immediately
|
||||||
|
s.getLocalPort(); // no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
long fdCount0 = getFdCount();
|
||||||
|
listProcFD();
|
||||||
|
|
||||||
|
// start a server
|
||||||
|
Server svr = new Server();
|
||||||
|
Thread thr = new Thread(svr);
|
||||||
|
thr.start();
|
||||||
|
|
||||||
|
Socket s = new Socket("localhost", svr.localPort());
|
||||||
|
pendingSockets.add(new NamedWeak(s, pendingQueue, "clientSocket"));
|
||||||
|
extractRefs(s, "clientSocket");
|
||||||
|
|
||||||
|
OutputStream out = s.getOutputStream();
|
||||||
|
out.write('x');
|
||||||
|
out.flush();
|
||||||
|
InputStream in = s.getInputStream();
|
||||||
|
int b = in.read(); // wait for it back
|
||||||
|
System.out.printf(" data sent and received%n");
|
||||||
|
// Do NOT close the Socket; forget it
|
||||||
|
|
||||||
|
Object ref;
|
||||||
|
int loops = 20;
|
||||||
|
while (!pendingSockets.isEmpty() && loops-- > 0) {
|
||||||
|
ref = pendingQueue.remove(1000L);
|
||||||
|
if (ref != null) {
|
||||||
|
pendingSockets.remove(ref);
|
||||||
|
System.out.printf(" ref queued: %s, remaining: %d%n", ref, pendingSockets.size());
|
||||||
|
} else {
|
||||||
|
s = null;
|
||||||
|
out = null;
|
||||||
|
in = null;
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thr.join();
|
||||||
|
|
||||||
|
// List the open file descriptors
|
||||||
|
long fdCount = getFdCount();
|
||||||
|
System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);
|
||||||
|
listProcFD();
|
||||||
|
|
||||||
|
if (loops == 0) {
|
||||||
|
throw new AssertionError("Not all references reclaimed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the count of open file descriptors, or -1 if not available
|
||||||
|
private static long getFdCount() {
|
||||||
|
OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
|
||||||
|
return (mxBean instanceof UnixOperatingSystemMXBean)
|
||||||
|
? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
|
||||||
|
: -1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflect to find references in the socket implementation that will be gc'd
|
||||||
|
private static void extractRefs(Socket s, String name) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Field socketImplField = Socket.class.getDeclaredField("impl");
|
||||||
|
socketImplField.setAccessible(true);
|
||||||
|
Object socketImpl = socketImplField.get(s);
|
||||||
|
|
||||||
|
Field fileDescriptorField = SocketImpl.class.getDeclaredField("fd");
|
||||||
|
fileDescriptorField.setAccessible(true);
|
||||||
|
FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
|
||||||
|
extractRefs(fileDescriptor, name);
|
||||||
|
|
||||||
|
Class<?> socketImplClass = socketImpl.getClass();
|
||||||
|
System.out.printf("socketImplClass: %s%n", socketImplClass);
|
||||||
|
if (socketImplClass.getClass().getName().equals("java.net.TwoStacksPlainSocketImpl")) {
|
||||||
|
Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
|
||||||
|
fileDescriptor1Field.setAccessible(true);
|
||||||
|
FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
|
||||||
|
extractRefs(fileDescriptor1, name + "::twoStacksFd1");
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new AssertionError("missing field", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void extractRefs(FileDescriptor fileDescriptor, String name) {
|
||||||
|
Object cleanup = null;
|
||||||
|
int rawfd = -1;
|
||||||
|
try {
|
||||||
|
if (fileDescriptor != null) {
|
||||||
|
Field fd1Field = FileDescriptor.class.getDeclaredField("fd");
|
||||||
|
fd1Field.setAccessible(true);
|
||||||
|
rawfd = fd1Field.getInt(fileDescriptor);
|
||||||
|
|
||||||
|
Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");
|
||||||
|
cleanupfdField.setAccessible(true);
|
||||||
|
cleanup = cleanupfdField.get(fileDescriptor);
|
||||||
|
pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
|
||||||
|
name + "::fileDescriptor: " + rawfd));
|
||||||
|
pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new AssertionError("missing field", ex);
|
||||||
|
} finally {
|
||||||
|
System.out.print(String.format(" fd: %s, fd: %d, cleanup: %s%n",
|
||||||
|
fileDescriptor, rawfd, cleanup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void extractRefs(ServerSocket s, String name) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Field socketImplField = ServerSocket.class.getDeclaredField("impl");
|
||||||
|
socketImplField.setAccessible(true);
|
||||||
|
Object socketImpl = socketImplField.get(s);
|
||||||
|
|
||||||
|
Field fileDescriptorField = SocketImpl.class.getDeclaredField("fd");
|
||||||
|
fileDescriptorField.setAccessible(true);
|
||||||
|
FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
|
||||||
|
|
||||||
|
Field fdField = FileDescriptor.class.getDeclaredField("fd");
|
||||||
|
fdField.setAccessible(true);
|
||||||
|
int rawfd = fdField.getInt(fileDescriptor);
|
||||||
|
|
||||||
|
Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");
|
||||||
|
cleanupField.setAccessible(true);
|
||||||
|
Object cleanup = cleanupField.get(fileDescriptor);
|
||||||
|
|
||||||
|
System.out.print(String.format(" fd: %s, fd: %d, cleanup: %s, socket: %s%n",
|
||||||
|
fileDescriptor, rawfd, cleanup, s));
|
||||||
|
|
||||||
|
pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
|
||||||
|
name + "::fileDescriptor: " + rawfd));
|
||||||
|
pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
|
||||||
|
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new AssertionError("missing field", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to list the open file descriptors (if supported by the 'lsof' command).
|
||||||
|
*/
|
||||||
|
static void listProcFD() {
|
||||||
|
List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");
|
||||||
|
Optional<Path> lsof = lsofDirs.stream()
|
||||||
|
.map(s -> Paths.get(s, "lsof"))
|
||||||
|
.filter(f -> Files.isExecutable(f))
|
||||||
|
.findFirst();
|
||||||
|
lsof.ifPresent(exe -> {
|
||||||
|
try {
|
||||||
|
System.out.printf("Open File Descriptors:%n");
|
||||||
|
long pid = ProcessHandle.current().pid();
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));
|
||||||
|
pb.inheritIO();
|
||||||
|
Process p = pb.start();
|
||||||
|
p.waitFor(10, TimeUnit.SECONDS);
|
||||||
|
} catch (IOException | InterruptedException ie) {
|
||||||
|
ie.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple class to identify which refs have been queued
|
||||||
|
static class NamedWeak extends WeakReference<Object> {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {
|
||||||
|
super(o, queue);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name + "; " + super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue