Not call java methods from within JNI init code to prevent class loading deadlocks.
Motivation: We used NetUtil.isIpV4StackPreferred() when loading JNI code which tries to load NetworkInterface in its static initializer. Unfortunally a lock on the NetworkInterface class init may be already hold somewhere else which may cause a loader deadlock. Modifications: Add a new Socket.initialize() method that will be called when init the library and pass everything needed to the JNI level so we not need to call back to java. Result: Fixes [#7458].
This commit is contained in:
parent
aabb73a9d2
commit
3f101caa4c
@ -17,17 +17,16 @@ package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.channel.DefaultFileRegion;
|
||||
import io.netty.channel.unix.Errors.NativeIoException;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.NativeLibraryLoader;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.NativeInetAddress;
|
||||
import io.netty.util.internal.ThrowableUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -40,9 +39,7 @@ import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupp
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.tcpMd5SigMaxKeyLen;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ioResult;
|
||||
import static io.netty.channel.unix.Errors.newConnectionResetException;
|
||||
import static io.netty.channel.unix.Errors.newIOException;
|
||||
@ -64,6 +61,7 @@ public final class Native {
|
||||
// The library was not previously loaded, load it now.
|
||||
loadNativeLibrary();
|
||||
}
|
||||
Socket.initialize();
|
||||
}
|
||||
|
||||
// EventLoop operations and constants
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.NativeLibraryLoader;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
@ -58,6 +59,7 @@ final class Native {
|
||||
// The library was not previously loaded, load it now.
|
||||
loadNativeLibrary();
|
||||
}
|
||||
Socket.initialize();
|
||||
}
|
||||
|
||||
static final short EV_ADD = evAdd();
|
||||
|
@ -40,9 +40,7 @@ static jclass datagramSocketAddressClass = NULL;
|
||||
static jmethodID datagramSocketAddrMethodId = NULL;
|
||||
static jmethodID inetSocketAddrMethodId = NULL;
|
||||
static jclass inetSocketAddressClass = NULL;
|
||||
static jclass netUtilClass = NULL;
|
||||
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
|
||||
static int socketType;
|
||||
static int socketType = AF_INET;
|
||||
static const char* ip4prefix = "::ffff:";
|
||||
static const unsigned char wildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
static const unsigned char ipv4MappedWildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
|
||||
@ -169,30 +167,26 @@ static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockadd
|
||||
return bArray;
|
||||
}
|
||||
|
||||
static int socket_type(JNIEnv* env) {
|
||||
jboolean ipv4Preferred = (*env)->CallStaticBooleanMethod(env, netUtilClass, netUtilClassIpv4PreferredMethodId);
|
||||
|
||||
static void netty_unix_socket_initialize(JNIEnv* env, jclass clazz, jboolean ipv4Preferred) {
|
||||
if (ipv4Preferred) {
|
||||
// User asked to use ipv4 explicitly.
|
||||
return AF_INET;
|
||||
}
|
||||
int fd = nettyNonBlockingSocket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
if (errno == EAFNOSUPPORT) {
|
||||
return AF_INET;
|
||||
}
|
||||
return AF_INET6;
|
||||
socketType = AF_INET;
|
||||
} else {
|
||||
// Explicitly try to bind to ::1 to ensure IPV6 can really be used.
|
||||
// See https://github.com/netty/netty/issues/7021.
|
||||
struct sockaddr_in6 addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */
|
||||
int res = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
int fd = nettyNonBlockingSocket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
socketType = errno == EAFNOSUPPORT ? AF_INET : AF_INET6;
|
||||
} else {
|
||||
// Explicitly try to bind to ::1 to ensure IPV6 can really be used.
|
||||
// See https://github.com/netty/netty/issues/7021.
|
||||
struct sockaddr_in6 addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */
|
||||
int res = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
|
||||
close(fd);
|
||||
return res == 0 ? AF_INET6 : AF_INET;
|
||||
close(fd);
|
||||
socketType = res == 0 ? AF_INET6 : AF_INET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,6 +867,8 @@ static jint netty_unix_socket_isBroadcast(JNIEnv* env, jclass clazz, jint fd) {
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
|
||||
// JNI Registered Methods End
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
@ -916,7 +912,8 @@ static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "getSendBufferSize", "(I)I", (void *) netty_unix_socket_getSendBufferSize },
|
||||
{ "getSoLinger", "(I)I", (void *) netty_unix_socket_getSoLinger },
|
||||
{ "getTrafficClass", "(I)I", (void *) netty_unix_socket_getTrafficClass },
|
||||
{ "getSoError", "(I)I", (void *) netty_unix_socket_getSoError }
|
||||
{ "getSoError", "(I)I", (void *) netty_unix_socket_getSoError },
|
||||
{ "initialize", "(Z)V", (void *) netty_unix_socket_initialize }
|
||||
};
|
||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||
|
||||
@ -1001,26 +998,6 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
|
||||
return JNI_ERR;
|
||||
}
|
||||
nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/util/NetUtil");
|
||||
jclass localNetUtilClass = (*env)->FindClass(env, nettyClassName);
|
||||
free(nettyClassName);
|
||||
nettyClassName = NULL;
|
||||
if (localNetUtilClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
netUtilClass = (jclass) (*env)->NewGlobalRef(env, localNetUtilClass);
|
||||
if (netUtilClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
netUtilClassIpv4PreferredMethodId = (*env)->GetStaticMethodID(env, netUtilClass, "isIpV4StackPreferred", "()Z" );
|
||||
if (netUtilClassIpv4PreferredMethodId == NULL) {
|
||||
// position method was not found.. something is wrong so bail out
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
void* mem = malloc(1);
|
||||
if (mem == NULL) {
|
||||
@ -1042,7 +1019,6 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
}
|
||||
free(mem);
|
||||
|
||||
socketType = socket_type(env);
|
||||
return NETTY_JNI_VERSION;
|
||||
}
|
||||
|
||||
@ -1055,8 +1031,4 @@ void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
|
||||
(*env)->DeleteGlobalRef(env, inetSocketAddressClass);
|
||||
inetSocketAddressClass = NULL;
|
||||
}
|
||||
if (netUtilClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, netUtilClass);
|
||||
netUtilClass = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package io.netty.channel.unix;
|
||||
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet6Address;
|
||||
@ -26,6 +27,7 @@ import java.net.PortUnreachableException;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERROR_ECONNREFUSED_NEGATIVE;
|
||||
@ -411,6 +413,8 @@ public class Socket extends FileDescriptor {
|
||||
'}';
|
||||
}
|
||||
|
||||
private static final AtomicBoolean INITIALIZED = new AtomicBoolean();
|
||||
|
||||
public static Socket newSocketStream() {
|
||||
return new Socket(newSocketStream0());
|
||||
}
|
||||
@ -423,6 +427,12 @@ public class Socket extends FileDescriptor {
|
||||
return new Socket(newSocketDomain0());
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
if (INITIALIZED.compareAndSet(false, true)) {
|
||||
initialize(NetUtil.isIpV4StackPreferred());
|
||||
}
|
||||
}
|
||||
|
||||
protected static int newSocketStream0() {
|
||||
int res = newSocketStreamFd();
|
||||
if (res < 0) {
|
||||
@ -498,4 +508,5 @@ public class Socket extends FileDescriptor {
|
||||
private static native void setSoLinger(int fd, int soLinger) throws IOException;
|
||||
private static native void setBroadcast(int fd, int broadcast) throws IOException;
|
||||
private static native void setTrafficClass(int fd, int trafficClass) throws IOException;
|
||||
private static native void initialize(boolean ipv4Preferred);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user