From 70e3d17620061767ce06f69e11a370fb61d69d08 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 29 May 2015 16:43:28 -0400 Subject: [PATCH] fixing small leak on exception on the transport-epoll-native allocation Motivation: the JNI function ThrowNew won't release any allocated memory. The method exceptionMessage is allocating a new string concatenating 2 constant strings What is creating a small leak in case of these exceptions are happening. Modifications: Added new methods that will use exceptionMessage and free resources accordingly. I am also removing the inline definition on these methods as they could be reused by other added modules (e.g. libaio which should be coming soon) Result: No more leaks in case of failures. --- .../src/main/c/exception_helper.h | 23 +++++++++ .../main/c/io_netty_channel_epoll_Native.c | 50 +++++++++++++------ 2 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 transport-native-epoll/src/main/c/exception_helper.h diff --git a/transport-native-epoll/src/main/c/exception_helper.h b/transport-native-epoll/src/main/c/exception_helper.h new file mode 100644 index 0000000000..d8c77078f5 --- /dev/null +++ b/transport-native-epoll/src/main/c/exception_helper.h @@ -0,0 +1,23 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +void throwRuntimeException(JNIEnv* env, char* message); +void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); +void throwIOException(JNIEnv* env, char* message); +void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); +void throwClosedChannelException(JNIEnv* env); +void throwOutOfMemoryError(JNIEnv* env); +char* exceptionMessage(char* msg, int error); diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c index 5f26e6935d..c12da87c28 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c @@ -33,6 +33,7 @@ #include #include #include "io_netty_channel_epoll_Native.h" +#include "exception_helper.h" /** * On older Linux kernels, epoll can't handle timeout @@ -93,25 +94,46 @@ static int socketType; static const char* ip4prefix = "::ffff:"; // util methods -static inline void throwRuntimeException(JNIEnv* env, char* message) { +void throwRuntimeException(JNIEnv* env, char* message) { (*env)->ThrowNew(env, runtimeExceptionClass, message); } -static inline void throwIOException(JNIEnv* env, char* message) { +void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { + char* allocatedMessage = exceptionMessage(message, errorNumber); + (*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage); + free(allocatedMessage); +} + +void throwIOException(JNIEnv* env, char* message) { (*env)->ThrowNew(env, ioExceptionClass, message); } -static inline void throwClosedChannelException(JNIEnv* env) { +void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { + char* allocatedMessage = exceptionMessage(message, errorNumber); + (*env)->ThrowNew(env, ioExceptionClass, allocatedMessage); + free(allocatedMessage); +} + +void throwClosedChannelException(JNIEnv* env) { jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId); (*env)->Throw(env, exception); } -static inline void throwOutOfMemoryError(JNIEnv* env) { +void throwOutOfMemoryError(JNIEnv* env) { jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); (*env)->ThrowNew(env, exceptionClass, ""); } -static inline char* exceptionMessage(char* msg, int error) { +/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */ +char* exceptionMessage(char* msg, int error) { + if (error < 0) { + // some functions return negative values + // and it's hard to keep track of when to send -error and when not + // this will just take care when things are forgotten + // what would generate a proper error + error = error * -1; + } + //strerror is returning a constant, so no need to free anything coming from strerror char* err = strerror(error); char* result = malloc(strlen(msg) + strlen(err) + 1); strcpy(result, msg); @@ -136,7 +158,7 @@ static inline jint getOption(JNIEnv* env, jint fd, int level, int optname, void* return 0; } int err = errno; - throwRuntimeException(env, exceptionMessage("getsockopt() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "getsockopt() failed: ", err); return code; } @@ -144,7 +166,7 @@ static inline int setOption(JNIEnv* env, jint fd, int level, int optname, const int rc = setsockopt(fd, level, optname, optval, len); if (rc < 0) { int err = errno; - throwRuntimeException(env, exceptionMessage("setsockopt() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "setsockopt() failed: ", err); } return rc; } @@ -615,7 +637,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_eventFd(JNIEnv* env, j if (eventFD < 0) { int err = errno; - throwRuntimeException(env, exceptionMessage("eventfd() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "eventfd() failed: ", err); } return eventFD; } @@ -625,7 +647,7 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv* e if (eventFD < 0) { int err = errno; - throwRuntimeException(env, exceptionMessage("eventfd_write() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "eventfd_write() failed: ", err); } } @@ -649,9 +671,9 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* en if (efd < 0) { int err = errno; if (epoll_create1) { - throwRuntimeException(env, exceptionMessage("epoll_create1() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "epoll_create1() failed: ", err); } else { - throwRuntimeException(env, exceptionMessage("epoll_create() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "epoll_create() failed: ", err); } return efd; } @@ -659,7 +681,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* en if (fcntl(efd, F_SETFD, FD_CLOEXEC) < 0) { int err = errno; close(efd); - throwRuntimeException(env, exceptionMessage("fcntl() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "fcntl() failed: ", err); return err; } } @@ -856,7 +878,7 @@ static inline jobject recvFrom0(JNIEnv* env, jint fd, void* buffer, jint pos, ji throwClosedChannelException(env); return NULL; } - throwIOException(env, exceptionMessage("recvfrom() failed: ", err)); + throwIOExceptionErrorNo(env, "recvfrom() failed: ", err); return NULL; } @@ -1375,7 +1397,7 @@ JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_kernelVersion(JNIEn return (*env)->NewStringUTF(env, name.release); } int err = errno; - throwRuntimeException(env, exceptionMessage("uname() failed: ", err)); + throwRuntimeExceptionErrorNo(env, "uname() failed: ", err); return NULL; }