Correctly handle overflow in Native.kevent(...) when EINTR is detected (#9024)

Motivation:
When kevent(...) returns with EINTR we do not correctly decrement the timespec
structure contents to account for the time duration. This may lead to negative
values for tv_nsec which will result in an EINVAL and raise an IOException to
the event loop selection loop.

Modifications:
Correctly calculate new timeoutTs when EINTR is detected

Result:
Fixes #9013.
This commit is contained in:
Scott Mitchell 2019-04-10 02:00:32 -07:00 committed by Norman Maurer
parent c0d3444f6d
commit ac023da16d
3 changed files with 39 additions and 8 deletions

View File

@ -139,15 +139,11 @@ static jint netty_kqueue_native_keventWait(JNIEnv* env, jclass clazz, jint kqueu
}
netty_unix_util_clock_gettime(waitClockId, &nowTs);
// beforeTs will store the time difference to check for overflow
beforeTs.tv_sec = nowTs.tv_sec - beforeTs.tv_sec;
beforeTs.tv_nsec = nowTs.tv_nsec - beforeTs.tv_nsec;
// Now subtract the time difference
timeoutTs.tv_sec -= beforeTs.tv_sec;
timeoutTs.tv_nsec -= beforeTs.tv_nsec;
if (beforeTs.tv_sec < 0 || beforeTs.tv_nsec < 0 || (timeoutTs.tv_sec <= 0 && timeoutTs.tv_nsec <= 0)) {
if (netty_unix_util_timespec_subtract_ns(&timeoutTs,
netty_unix_util_timespec_elapsed_ns(&beforeTs, &nowTs))) {
return 0;
}
beforeTs = nowTs;
// https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
// When kevent() call fails with EINTR error, all changes in the changelist have been applied.

View File

@ -19,11 +19,12 @@
#include <errno.h>
#include "netty_unix_util.h"
static const uint64_t NETTY_BILLION = 1000000000L;
#ifdef NETTY_USE_MACH_INSTEAD_OF_CLOCK
#include <mach/mach.h>
#include <mach/mach_time.h>
static const uint64_t NETTY_BILLION = 1000000000L;
#endif /* NETTY_USE_MACH_INSTEAD_OF_CLOCK */
@ -101,6 +102,25 @@ char* netty_unix_util_parse_package_prefix(const char* libraryPathName, const ch
}
// util methods
uint64_t netty_unix_util_timespec_elapsed_ns(const struct timespec* begin, const struct timespec* end) {
return NETTY_BILLION * (end->tv_sec - begin->tv_sec) + (end->tv_nsec - begin->tv_nsec);
}
jboolean netty_unix_util_timespec_subtract_ns(struct timespec* ts, uint64_t nanos) {
const uint64_t seconds = nanos / NETTY_BILLION;
nanos -= seconds * NETTY_BILLION;
// If there are too many nanos we steal from seconds to avoid underflow on nanos. This way we
// only have to worry about underflow on tv_sec.
if (nanos > ts->tv_nsec) {
--(ts->tv_sec);
ts->tv_nsec += NETTY_BILLION;
}
const jboolean underflow = ts->tv_sec < seconds;
ts->tv_sec -= seconds;
ts->tv_nsec -= nanos;
return underflow;
}
int netty_unix_util_clock_gettime(clockid_t clockId, struct timespec* tp) {
#ifdef NETTY_USE_MACH_INSTEAD_OF_CLOCK
uint64_t timeNs;

View File

@ -18,6 +18,7 @@
#define NETTY_UNIX_UTIL_H_
#include <jni.h>
#include <stdint.h>
#include <time.h>
#if defined(__MACH__) && !defined(CLOCK_REALTIME)
@ -64,6 +65,20 @@ jboolean netty_unix_util_initialize_wait_clock(clockid_t* clockId);
*/
int netty_unix_util_clock_gettime(clockid_t clockId, struct timespec* tp);
/**
* Calculate the number of nano seconds elapsed between begin and end.
*
* Returns the number of nano seconds.
*/
uint64_t netty_unix_util_timespec_elapsed_ns(const struct timespec* begin, const struct timespec* end);
/**
* Subtract <pre>nanos</pre> nano seconds from a <pre>timespec</pre>.
*
* Returns true if there is underflow.
*/
jboolean netty_unix_util_timespec_subtract_ns(struct timespec* ts, uint64_t nanos);
/**
* Return type is as defined in http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp5833.
*/