diff --git a/junixsocket-native/pom.xml b/junixsocket-native/pom.xml
index fa7e51ef1..8ee10e9d1 100644
--- a/junixsocket-native/pom.xml
+++ b/junixsocket-native/pom.xml
@@ -35,6 +35,52 @@
+
+ polling-eintr-test
+
+
+ polling-eintr-test
+
+
+
+
+
+ com.github.maven-nar
+ nar-maven-plugin
+
+
+ ${junixsocket.native.default.toolPath}
+
+ **/*.c
+
+
+
+ ${junixsocket.native.default.toolPath}
+
+ ${junixsocket.native.default.linkerName}
+
+
+
+ polling-eintr-test
+ shared
+
+
+
+
+
+
+ default-nar-testCompile
+
+
+ true
+
+
+
+
+
+
+
+
current-arch
diff --git a/junixsocket-native/src/main/c/polling.c b/junixsocket-native/src/main/c/polling.c
index e2f966d66..42eff8110 100644
--- a/junixsocket-native/src/main/c/polling.c
+++ b/junixsocket-native/src/main/c/polling.c
@@ -18,6 +18,7 @@
#include "config.h"
#include "polling.h"
+#include "polling_with_deadline.h"
#include "exceptions.h"
#include "filedescriptors.h"
@@ -28,11 +29,6 @@ CK_IGNORE_CPP_WARNINGS_BEGIN
#if __TOS_MVS__
# include
# include
-# include "gettod_zos.h"
-static int clock_gettime(int mode, struct timespec* out) {
- return gettimeofdayMonotonic(out);
-}
-# define CLOCK_MONOTONIC 1
#endif
static jclass class_PollFd = NULL;
@@ -87,10 +83,6 @@ static int eventToOp(int event) {
#if defined(junixsocket_use_poll_for_accept) || defined(junixsocket_use_poll_for_read)
-static uint64_t timespecToMillis(struct timespec* ts) {
- return (uint64_t)ts->tv_sec * 1000 + (uint64_t)ts->tv_nsec / 1000000;
-}
-
/*
* Waits until the connection is ready to read/accept.
*
@@ -187,7 +179,7 @@ jint pollWithMillis(int handle, uint64_t millis) {
struct timespec timeStart;
struct timespec timeEnd;
- if(clock_gettime(CLOCK_MONOTONIC, &timeEnd) == -1) {
+ if(pollMonotonicTime(&timeEnd) == -1) {
return -1;
}
int ret;
@@ -220,10 +212,10 @@ jint pollWithMillis(int handle, uint64_t millis) {
}
}
int errnum = socket_errno;
- if(clock_gettime(CLOCK_MONOTONIC, &timeEnd) == -1) {
+ if(pollMonotonicTime(&timeEnd) == -1) {
return -1;
}
- int elapsed = (int)(timespecToMillis(&timeEnd) - timespecToMillis(&timeStart));
+ int elapsed = (int)(pollTimespecToMillis(&timeEnd) - pollTimespecToMillis(&timeStart));
if(elapsed <= 0) {
elapsed = 1;
}
@@ -418,7 +410,7 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_poll
#if defined(_WIN32)
ret = WSAPoll(pollFd, nfds, timeout);
#else
- ret = poll(pollFd, nfds, timeout);
+ ret = pollWithDeadline(pollFd, nfds, timeout);
#endif
if(ret == -1) {
ret = 0;
diff --git a/junixsocket-native/src/main/c/polling_with_deadline.h b/junixsocket-native/src/main/c/polling_with_deadline.h
new file mode 100644
index 000000000..2030c4252
--- /dev/null
+++ b/junixsocket-native/src/main/c/polling_with_deadline.h
@@ -0,0 +1,76 @@
+/*
+ * junixsocket
+ *
+ * Copyright 2009-2026 Christian Kohlschütter
+ *
+ * Licensed 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.
+ */
+
+#ifndef polling_with_deadline_h
+#define polling_with_deadline_h
+
+#include "config.h"
+
+#if __TOS_MVS__
+#include "gettod_zos.h"
+#endif
+
+static int pollMonotonicTime(struct timespec* out) {
+#if __TOS_MVS__
+ return gettimeofdayMonotonic(out);
+#else
+ return clock_gettime(CLOCK_MONOTONIC, out);
+#endif
+}
+
+static uint64_t pollTimespecToMillis(const struct timespec* ts) {
+ return (uint64_t)ts->tv_sec * 1000 + (uint64_t)ts->tv_nsec / 1000000;
+}
+
+#if !defined(_WIN32)
+
+static int pollWithDeadline(struct pollfd* pollFd, nfds_t nfds, int timeout) {
+ uint64_t deadlineMillis = 0;
+
+ if(timeout > 0) {
+ struct timespec now;
+ if(pollMonotonicTime(&now) == -1) {
+ return -1;
+ }
+ deadlineMillis = pollTimespecToMillis(&now) + (uint64_t)timeout;
+ }
+
+ for(;;) {
+ int ret = poll(pollFd, nfds, timeout);
+ if(ret != -1 || socket_errno != EINTR) {
+ return ret;
+ }
+
+ if(timeout > 0) {
+ struct timespec now;
+ if(pollMonotonicTime(&now) == -1) {
+ return -1;
+ }
+
+ uint64_t nowMillis = pollTimespecToMillis(&now);
+ if(nowMillis >= deadlineMillis) {
+ return 0;
+ }
+ timeout = (int)(deadlineMillis - nowMillis);
+ }
+ }
+}
+
+#endif /* !_WIN32 */
+
+#endif /* polling_with_deadline_h */
diff --git a/junixsocket-native/src/test/c/polling-eintr-test.c b/junixsocket-native/src/test/c/polling-eintr-test.c
new file mode 100644
index 000000000..83ad4463b
--- /dev/null
+++ b/junixsocket-native/src/test/c/polling-eintr-test.c
@@ -0,0 +1,123 @@
+/*
+ * junixsocket
+ *
+ * Copyright 2009-2026 Christian Kohlschütter
+ *
+ * Licensed 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.
+ */
+
+#if defined(_WIN32)
+
+int main(void) {
+ return 0;
+}
+
+#else
+
+#include
+#include
+#include
+#include
+#include
+
+#include "../../main/c/polling_with_deadline.h"
+
+static volatile sig_atomic_t signalReceived = 0;
+
+static void signalHandler(int signalNumber CK_UNUSED) {
+ signalReceived = 1;
+}
+
+static void sleepMillis(long millis) {
+ struct timespec delay = {
+ .tv_sec = millis / 1000,
+ .tv_nsec = (millis % 1000) * 1000000,
+ };
+ while(nanosleep(&delay, &delay) == -1 && errno == EINTR) {
+ }
+}
+
+static int pipeWriteFd = -1;
+
+static void writeAfterDeadline(void) {
+ sleepMillis(2500);
+ char value = 1;
+ if(write(pipeWriteFd, &value, sizeof(value)) != sizeof(value)) {
+ _exit(EXIT_FAILURE);
+ }
+}
+
+static int waitForChild(pid_t child) {
+ int status;
+ while(waitpid(child, &status, 0) == -1) {
+ if(errno != EINTR) {
+ return -1;
+ }
+ }
+ return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS ? 0 : -1;
+}
+
+int main(void) {
+ int pipeFds[2];
+ if(pipe(pipeFds) == -1) {
+ perror("pipe");
+ return EXIT_FAILURE;
+ }
+ pipeWriteFd = pipeFds[1];
+
+ struct sigaction action = {
+ .sa_handler = signalHandler,
+ };
+ sigemptyset(&action.sa_mask);
+ if(sigaction(SIGALRM, &action, NULL) == -1) {
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+
+ pid_t writerChild = fork();
+ if(writerChild == -1) {
+ perror("fork");
+ return EXIT_FAILURE;
+ } else if(writerChild == 0) {
+ writeAfterDeadline();
+ _exit(EXIT_SUCCESS);
+ }
+
+ struct pollfd pollFd = {
+ .fd = pipeFds[0],
+ .events = POLLIN,
+ };
+ alarm(1);
+ int ret = pollWithDeadline(&pollFd, 1, 2000);
+ alarm(0);
+
+ int childFailed = waitForChild(writerChild);
+ close(pipeFds[0]);
+ close(pipeFds[1]);
+
+ if(childFailed != 0) {
+ fprintf(stderr, "The writer child failed\n");
+ return EXIT_FAILURE;
+ }
+ if(!signalReceived) {
+ fprintf(stderr, "SIGALRM was not received\n");
+ return EXIT_FAILURE;
+ }
+ if(ret != 0) {
+ fprintf(stderr, "Expected timeout after EINTR, got poll result %d\n", ret);
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+#endif