#include <uk/print.h>
#include <uk/spinlock.h>
#include <uk/plat/lcpu.h>
+#include <uk/plat/time.h>
/** @struct uk_futex
* @brief Futex structure.
* 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;
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);
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: