]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/posix-futex: Implement subset of FUTEX_WAIT_BITSET
authorMarco Schlumpp <marco@unikraft.io>
Thu, 9 Feb 2023 15:44:35 +0000 (16:44 +0100)
committerUnikraft <monkey@unikraft.io>
Sun, 7 May 2023 12:18:32 +0000 (12:18 +0000)
Many modern applications use FUTEX_WAIT_BITSET to be able to set an
absolute timeout. To accomplish that they set all bits in the val3/mask
parameter, which makes the operation equivalent to a normal FUTEX_WAIT
(except the absolute timeout value).

Signed-off-by: Marco Schlumpp <marco@unikraft.io>
Reviewed-by: Adina Smeu <adina.smeu@gmail.com>
Approved-by: Razvan Deaconescu <razvand@unikraft.io>
Tested-by: Unikraft CI <monkey@unikraft.io>
GitHub-Closes: #758

lib/posix-futex/futex.c

index ab53f69007a49a9a1776eb4c57dfdb6f0550d988..9c0c26c36c82dcdf51862ac182a5c728e8f722f2 100644 (file)
@@ -59,6 +59,7 @@
 #include <uk/print.h>
 #include <uk/spinlock.h>
 #include <uk/plat/lcpu.h>
+#include <uk/plat/time.h>
 
 /** @struct uk_futex
  *  @brief Futex structure.
@@ -82,18 +83,17 @@ static uk_spinlock futex_list_lock = UK_SPINLOCK_INITIALIZER();
  * expected one. If the futex was not removed from the list when the thread was
  * unblocked, then it means that it timed out.
  *
- * @param uaddr        The futex userspace address
- * @param val  The expected value
- * @param tm   Timespec structure which contains the timeout for which the
- *             thread will blocked. If it is null, the thread will wait
- *             indefinitely.
+ * @param uaddr                The futex userspace address
+ * @param val          The expected value
+ * @param timeout      The deadline until the function will block at most.
+ *                     If it is NULL, the thread will wait indefinitely.
  *
  * @return
  *     0: uaddr contains val and the thread finished waiting;
  *     <1: -EAGAIN (uaddr does not contain val) or -ETIMEDOUT (the futex timed
  *       out)
  */
-static int futex_wait(uint32_t *uaddr, uint32_t val, const struct timespec *tm)
+static int futex_wait(uint32_t *uaddr, uint32_t val, const __nsec *timeout)
 {
        unsigned long irqf;
        struct uk_list_head *itr, *tmp;
@@ -111,9 +111,9 @@ static int futex_wait(uint32_t *uaddr, uint32_t val, const struct timespec *tm)
                uk_spin_unlock(&futex_list_lock);
                ukplat_lcpu_restore_irqf(irqf);
 
-               if (tm)
-                       /* Block for at most timeout nanosecs */
-                       uk_thread_block_timeout(current, tm->tv_nsec);
+               if (timeout)
+                       /* Block at most until `timeout` nanosecs */
+                       uk_thread_block_until(current, (__snsec)*timeout);
                else
                        /* Block indefinitely */
                        uk_thread_block(current);
@@ -276,10 +276,42 @@ UK_LLSYSCALL_R_DEFINE(int, futex, uint32_t *, uaddr, int, futex_op,
                      uint32_t, val, const struct timespec *, timeout,
                      uint32_t *, uaddr2, uint32_t, val3)
 {
+       __nsec timeout_ns;
+
+       /*
+        * N.B. CLOCK_(MONOTONIC|REALTIME|...) are at the moment all the same in
+        * Unikraft. Therefore, we can just use CLOCK_MONOTONIC for timeouts in
+        * the following code.
+        */
        switch (futex_op) {
        case FUTEX_WAIT:
        case FUTEX_WAIT_PRIVATE:
-               return futex_wait(uaddr, val, timeout);
+               /*
+                * `timeout` is relative to "now" (whenever that is). To
+                * simplify the implementation regarding overflows, we will only
+                * honor the nanosecond part of the timespec.
+                */
+               if (timeout)
+                       timeout_ns = ukplat_monotonic_clock() +
+                                    ukarch_time_sec_to_nsec(timeout->tv_sec) +
+                                    timeout->tv_nsec;
+               return futex_wait(uaddr, val, timeout ? &timeout_ns : NULL);
+
+       case FUTEX_WAIT_BITSET:
+       case FUTEX_WAIT_BITSET_PRIVATE:
+               /*
+                * Special case implementation for cases where the val3/mask has
+                * all bits set. Some applications do this to use the absolute
+                * timeout mode. We return ENOSYS for all non-supported calls.
+                */
+               if (val3 != UINT32_MAX)
+                       return -ENOSYS;
+
+               if (timeout)
+                       timeout_ns = ukarch_time_sec_to_nsec(timeout->tv_sec)
+                                    + timeout->tv_nsec;
+
+               return futex_wait(uaddr, val, timeout ? &timeout_ns : NULL);
 
        case FUTEX_WAKE:
        case FUTEX_WAKE_PRIVATE: