Module Name:    src
Committed By:   riastradh
Date:           Wed Mar  5 14:01:55 UTC 2025

Modified Files:
        src/sys/kern: sys_futex.c

Log Message:
futex(2): Avoid returning early on timeout.

Rounding in the arithmetic leading into cv_timedwait_sig, and any
skew between the timecounter used by clock_gettime and the hardclock
timer used to wake cv_timedwait_sig, can lead cv_timedwait_sig to
wake up before the deadline as observable by clock_gettime.

futex(FUTEX_WAIT) is not supposed to do that, so ignore when
cv_timedwait_sig returns EWOULDBLOCK -- we'll notice the deadline has
passed in the next iteration anyway, if it has actually passed.

While here, make sure that we never pass less than 1 tick to
cv_timedwait_sig -- that turns it into cv_wait_sig, to wait
indefinitely with no timeout.

With this change, I have not seen any failures as reported in:

PR kern/59132: t_futex_ops:futex_wait_timeout_* sometimes fails on
early wakeup

Some instrumentation in futex_wait to count when cv_timedwait_sig
returns early as measured by clock_gettime (not committed in this
change, just local experiments) supports this hypothesis for the
symptoms observed in the PR.


To generate a diff of this commit:
cvs rdiff -u -r1.25 -r1.26 src/sys/kern/sys_futex.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/sys_futex.c
diff -u src/sys/kern/sys_futex.c:1.25 src/sys/kern/sys_futex.c:1.26
--- src/sys/kern/sys_futex.c:1.25	Wed Mar  5 14:01:34 2025
+++ src/sys/kern/sys_futex.c	Wed Mar  5 14:01:55 2025
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_futex.c,v 1.25 2025/03/05 14:01:34 riastradh Exp $	*/
+/*	$NetBSD: sys_futex.c,v 1.26 2025/03/05 14:01:55 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018, 2019, 2020 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.25 2025/03/05 14:01:34 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.26 2025/03/05 14:01:55 riastradh Exp $");
 
 /*
  * Futexes
@@ -966,9 +966,17 @@ futex_wait(struct futex_wait *fw, const 
 			/* Count how much time is left.  */
 			timespecsub(deadline, &ts, &ts);
 
-			/* Wait for that much time, allowing signals.  */
+			/*
+			 * Wait for that much time, allowing signals.
+			 * Ignore EWOULDBLOCK, however: we will detect
+			 * timeout ourselves on the next iteration of
+			 * the loop -- and the timeout may have been
+			 * truncated by tstohz, anyway.
+			 */
 			error = cv_timedwait_sig(&fw->fw_cv, &fw->fw_lock,
-			    tstohz(&ts));
+			    MAX(1, tstohz(&ts)));
+			if (error == EWOULDBLOCK)
+				error = 0;
 		} else {
 			/* Wait indefinitely, allowing signals. */
 			error = cv_wait_sig(&fw->fw_cv, &fw->fw_lock);
@@ -978,14 +986,10 @@ futex_wait(struct futex_wait *fw, const 
 	/*
 	 * If we were woken up, the waker will have removed fw from the
 	 * queue.  But if anything went wrong, we must remove fw from
-	 * the queue ourselves.  While here, convert EWOULDBLOCK to
-	 * ETIMEDOUT.
+	 * the queue ourselves.
 	 */
-	if (error) {
+	if (error)
 		futex_wait_abort(fw);
-		if (error == EWOULDBLOCK)
-			error = ETIMEDOUT;
-	}
 
 	mutex_exit(&fw->fw_lock);
 

Reply via email to