[PATCH] pselect: Use linux pselect syscall when available

"Nicolas S. Dade" <[email protected]> Wed, 16 Dec 2015 16:57:08 -0800
Newsgroups gmane.comp.lib.uclibc.general
Message-ID <[email protected]>
Linux has a pselect syscall since 2.6.something. Using it
rather than emulating it with sigprocmask+select+sigprocmask
is smaller code, and works properly. (The emulation has
race conditions when unblocked signals arrive before or
after the select)

The tv.nsec >= 1E9 handling comes from uclibc's linux select()
implementation, which itself uses pselect() internally if the
pselect syscall exists. I though it would be good to do the
same here.

Signed-off-by: Nicolas S. Dade <[email protected]>
---
 libc/sysdeps/linux/common/pselect.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/libc/sysdeps/linux/common/pselect.c b/libc/sysdeps/linux/common/pselect.c
index bf19ce3..928577e 100644
--- a/libc/sysdeps/linux/common/pselect.c
+++ b/libc/sysdeps/linux/common/pselect.c
@@ -30,6 +30,32 @@ static int __NC(pselect)(int nfds, fd_set *readfds, fd_set *writefds,
 			 fd_set *exceptfds, const struct timespec *timeout,
 			 const sigset_t *sigmask)
 {
+#ifdef __NR_pselect6
+#define NSEC_PER_SEC 1000000000L
+	struct timespec _ts, *ts = 0;
+	if (timeout) {
+		/* The Linux kernel can in some situations update the timeout value.
+		 * We do not want that so use a local variable.
+		 */
+		_ts.tv_sec = timeout->tv_sec;
+		_ts.tv_nsec = timeout->tv_nsec;
+
+		/* GNU extension: allow for timespec values where the sub-sec
+		* field is equal to or more than 1 second.  The kernel will
+		* reject this on us, so take care of the time shift ourself.
+		* Some applications (like readline and linphone) do this.
+		* See 'clarification on select() type calls and invalid timeouts'
+		* on the POSIX general list for more information.
+		*/
+		if (_ts.tv_nsec >= NSEC_PER_SEC) {
+			_ts.tv_sec += _ts.tv_nsec / NSEC_PER_SEC;
+			_ts.tv_nsec %= NSEC_PER_SEC;
+		}
+
+		ts = &_ts;
+	}
+	return INLINE_SYSCALL(pselect6, 6, nfds, readfds, writefds, exceptfds, ts, sigmask);
+#else
 	struct timeval tval;
 	int retval;
 	sigset_t savemask;
@@ -57,6 +83,7 @@ static int __NC(pselect)(int nfds, fd_set *readfds, fd_set *writefds,
 		sigprocmask (SIG_SETMASK, &savemask, NULL);
 
 	return retval;
+#endif
 }
 CANCELLABLE_SYSCALL(int, pselect, (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
 				   const struct timespec *timeout, const sigset_t *sigmask),
-- 
1.9.1