Currently, dopselect() and doppoll() call tsleep_nsec() without retry.
cheloha@ asked if the functions should handle spurious wakeups. I guess
such wakeups are unlikely with the nowake wait channel, but I am not
sure if that is a safe guess.

The following diff adds the retrying. The code is a bit arduous, so the
retry loop is put in a separate function that both poll and select use.

Index: kern/sys_generic.c
===================================================================
RCS file: src/sys/kern/sys_generic.c,v
retrieving revision 1.141
diff -u -p -r1.141 sys_generic.c
--- kern/sys_generic.c  16 Nov 2021 13:48:23 -0000      1.141
+++ kern/sys_generic.c  16 Nov 2021 13:50:08 -0000
@@ -90,6 +90,7 @@ int dopselect(struct proc *, int, fd_set
 int doppoll(struct proc *, struct pollfd *, u_int, struct timespec *,
     const sigset_t *, register_t *);
 void doselwakeup(struct selinfo *);
+int selsleep(struct timespec *);
 
 int
 iovec_copyin(const struct iovec *uiov, struct iovec **iovp, struct iovec *aiov,
@@ -664,19 +665,7 @@ dopselect(struct proc *p, int nd, fd_set
         * there's nothing to wait for.
         */
        if (nevents == 0 && ncollected == 0) {
-               uint64_t nsecs = INFSLP;
-
-               if (timeout != NULL) {
-                       if (!timespecisset(timeout))
-                               goto done;
-                       nsecs = MAX(1, MIN(TIMESPEC_TO_NSEC(timeout), MAXTSLP));
-               }
-               error = tsleep_nsec(&nowake, PSOCK | PCATCH, "kqsel", nsecs);
-               /* select is not restarted after signals... */
-               if (error == ERESTART)
-                       error = EINTR;
-               if (error == EWOULDBLOCK)
-                       error = 0;
+               error = selsleep(timeout);
                goto done;
        }
 
@@ -849,6 +838,46 @@ selfalse(dev_t dev, int events, struct p
 }
 
 /*
+ * Sleep until a signal arrives or the optional timeout expires.
+ */
+int
+selsleep(struct timespec *timeout)
+{
+       uint64_t end, now, nsecs;
+       int error;
+
+       if (timeout != NULL) {
+               if (!timespecisset(timeout))
+                       return (0);
+               now = getnsecuptime();
+               end = MIN(now + TIMESPEC_TO_NSEC(timeout), MAXTSLP);
+               if (end < now)
+                       end = MAXTSLP;
+       }
+
+       do {
+               if (timeout != NULL)
+                       nsecs = MAX(1, end - now);
+               else
+                       nsecs = INFSLP;
+               error = tsleep_nsec(&nowake, PSOCK | PCATCH, "selslp", nsecs);
+               if (timeout != NULL) {
+                       now = getnsecuptime();
+                       if (now >= end)
+                               break;
+               }
+       } while (error == 0);
+
+       /* poll/select is not restarted after signals... */
+       if (error == ERESTART)
+               error = EINTR;
+       if (error == EWOULDBLOCK)
+               error = 0;
+
+       return (error);
+}
+
+/*
  * Record a select request.
  */
 void
@@ -1158,19 +1187,7 @@ doppoll(struct proc *p, struct pollfd *f
         * there's nothing to wait for.
         */
        if (nevents == 0 && ncollected == 0) {
-               uint64_t nsecs = INFSLP;
-
-               if (timeout != NULL) {
-                       if (!timespecisset(timeout))
-                               goto done;
-                       nsecs = MAX(1, MIN(TIMESPEC_TO_NSEC(timeout), MAXTSLP));
-               }
-
-               error = tsleep_nsec(&nowake, PSOCK | PCATCH, "kqpoll", nsecs);
-               if (error == ERESTART)
-                       error = EINTR;
-               if (error == EWOULDBLOCK)
-                       error = 0;
+               error = selsleep(timeout);
                goto done;
        }
 

Reply via email to