Changeset b59318e in mainline


Ignore:
Timestamp:
2018-06-26T17:34:48Z (6 years ago)
Author:
Jiří Zárevúcky <jiri.zarevucky@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
ab6edb6
Parents:
f6372be9
git-author:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-06-26 17:01:43)
git-committer:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-06-26 17:34:48)
Message:

Make futex able to time out.

Files:
8 edited

Legend:

Unmodified
Added
Removed
  • abi/include/abi/synch.h

    rf6372be9 rb59318e  
    4545/** Interruptible operation. */
    4646#define SYNCH_FLAGS_INTERRUPTIBLE  (1 << 1)
     47/** Futex operation (makes sleep with timeout composable). */
     48#define SYNCH_FLAGS_FUTEX          (1 << 2)
    4749
    4850#endif
  • kernel/generic/include/proc/thread.h

    rf6372be9 rb59318e  
    112112        /** If true, the thread can be interrupted from sleep. */
    113113        bool sleep_interruptible;
     114
     115        /**
     116         * If true, and this thread's sleep returns without a wakeup
     117         * (timed out or interrupted), waitq ignores the next wakeup.
     118         * This is necessary for futex to be able to handle those conditions.
     119         */
     120        bool sleep_composable;
     121
    114122        /** Wait queue in which this thread sleeps. */
    115123        waitq_t *sleep_queue;
  • kernel/generic/include/synch/futex.h

    rf6372be9 rb59318e  
    5353
    5454extern void futex_init(void);
    55 extern sys_errno_t sys_futex_sleep(uintptr_t);
     55extern sys_errno_t sys_futex_sleep(uintptr_t, uintptr_t);
    5656extern sys_errno_t sys_futex_wakeup(uintptr_t);
    5757
  • kernel/generic/include/synch/waitq.h

    rf6372be9 rb59318e  
    6262        int missed_wakeups;
    6363
     64        /** Number of wakeups that need to be ignored due to futex timeout. */
     65        int ignore_wakeups;
     66
    6467        /** List of sleeping threads for which there was no missed_wakeup. */
    6568        list_t sleepers;
  • kernel/generic/src/proc/thread.c

    rf6372be9 rb59318e  
    383383        timeout_initialize(&thread->sleep_timeout);
    384384        thread->sleep_interruptible = false;
     385        thread->sleep_composable = false;
    385386        thread->sleep_queue = NULL;
    386387        thread->timeout_pending = false;
  • kernel/generic/src/synch/futex.c

    rf6372be9 rb59318e  
    398398}
    399399
    400 /** Sleep in futex wait queue.
    401  *
    402  * @param uaddr         Userspace address of the futex counter.
     400/** Sleep in futex wait queue with a timeout.
     401 *  If the sleep times out or is interrupted, the next wakeup is ignored.
     402 *  The userspace portion of the call must handle this condition.
     403 *
     404 * @param uaddr         Userspace address of the futex counter.
     405 * @param timeout       Maximum number of useconds to sleep. 0 means no limit.
    403406 *
    404407 * @return              If there is no physical mapping for uaddr ENOENT is
     
    406409 *                      waitq_sleep_timeout().
    407410 */
    408 sys_errno_t sys_futex_sleep(uintptr_t uaddr)
     411sys_errno_t sys_futex_sleep(uintptr_t uaddr, uintptr_t timeout)
    409412{
    410413        futex_t *futex = get_futex(uaddr);
     
    417420#endif
    418421
    419         errno_t rc = waitq_sleep_timeout(
    420             &futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE, NULL);
     422        errno_t rc = waitq_sleep_timeout(&futex->wq, timeout,
     423            SYNCH_FLAGS_INTERRUPTIBLE | SYNCH_FLAGS_FUTEX, NULL);
    421424
    422425#ifdef CONFIG_UDEBUG
  • kernel/generic/src/synch/waitq.c

    rf6372be9 rb59318e  
    5757#include <adt/list.h>
    5858#include <arch/cycle.h>
     59#include <mem.h>
    5960
    6061static void waitq_sleep_timed_out(void *);
     
    7172void waitq_initialize(waitq_t *wq)
    7273{
     74        memsetb(wq, sizeof(*wq), 0);
    7375        irq_spinlock_initialize(&wq->lock, "wq.lock");
    7476        list_initialize(&wq->sleepers);
    75         wq->missed_wakeups = 0;
    7677}
    7778
     
    114115                thread->saved_context = thread->sleep_timeout_context;
    115116                do_wakeup = true;
     117                if (thread->sleep_composable)
     118                        wq->ignore_wakeups++;
    116119                thread->sleep_queue = NULL;
    117120                irq_spinlock_unlock(&wq->lock, false);
     
    176179                list_remove(&thread->wq_link);
    177180                thread->saved_context = thread->sleep_interruption_context;
     181                if (thread->sleep_composable)
     182                        wq->ignore_wakeups++;
    178183                do_wakeup = true;
    179184                thread->sleep_queue = NULL;
     
    393398         */
    394399        irq_spinlock_lock(&THREAD->lock, false);
     400
     401        THREAD->sleep_composable = (flags & SYNCH_FLAGS_FUTEX);
    395402
    396403        if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
     
    538545        assert(irq_spinlock_locked(&wq->lock));
    539546
     547        if (wq->ignore_wakeups > 0) {
     548                if (mode == WAKEUP_FIRST) {
     549                        wq->ignore_wakeups--;
     550                        return;
     551                }
     552                wq->ignore_wakeups = 0;
     553        }
     554
    540555loop:
    541556        if (list_empty(&wq->sleepers)) {
  • uspace/lib/c/include/futex.h

    rf6372be9 rb59318e  
    3636#define LIBC_FUTEX_H_
    3737
     38#include <assert.h>
    3839#include <atomic.h>
    3940#include <errno.h>
     
    9899}
    99100
    100 /** Down the futex.
     101/** Down the futex with timeout, composably.
     102 *
     103 * This means that when the operation fails due to a timeout or being
     104 * interrupted, the next futex_up() is ignored, which allows certain kinds of
     105 * composition of synchronization primitives.
     106 *
     107 * In most other circumstances, regular futex_down_timeout() is a better choice.
    101108 *
    102109 * @param futex Futex.
    103110 *
    104111 * @return ENOENT if there is no such virtual address.
     112 * @return ETIMEOUT if timeout expires.
    105113 * @return EOK on success.
    106114 * @return Error code from <errno.h> otherwise.
    107115 *
    108116 */
    109 static inline errno_t futex_down(futex_t *futex)
     117static inline errno_t futex_down_composable(futex_t *futex, struct timeval *expires)
    110118{
     119        // TODO: Add tests for this.
     120
     121        /* No timeout by default. */
     122        suseconds_t timeout = 0;
     123
     124        if (expires) {
     125                struct timeval tv;
     126                getuptime(&tv);
     127                if (tv_gteq(&tv, expires)) {
     128                        /* We can't just return ETIMEOUT. That wouldn't be composable. */
     129                        timeout = 1;
     130                } else {
     131                        timeout = tv_sub_diff(expires, &tv);
     132                }
     133
     134                assert(timeout > 0);
     135        }
     136
    111137        if ((atomic_signed_t) atomic_predec(&futex->val) < 0)
    112                 return (errno_t) __SYSCALL1(SYS_FUTEX_SLEEP, (sysarg_t) &futex->val.count);
     138                return (errno_t) __SYSCALL2(SYS_FUTEX_SLEEP, (sysarg_t) &futex->val.count, (sysarg_t) timeout);
    113139
    114140        return EOK;
     
    132158}
    133159
     160static inline errno_t futex_down_timeout(futex_t *futex, struct timeval *expires)
     161{
     162        /*
     163         * This combination of a "composable" sleep followed by futex_up() on
     164         * failure is necessary to prevent breakage due to certain race
     165         * conditions.
     166         */
     167        errno_t rc = futex_down_composable(futex, expires);
     168        if (rc != EOK)
     169                futex_up(futex);
     170        return rc;
     171}
     172
     173/** Down the futex.
     174 *
     175 * @param futex Futex.
     176 *
     177 * @return ENOENT if there is no such virtual address.
     178 * @return EOK on success.
     179 * @return Error code from <errno.h> otherwise.
     180 *
     181 */
     182static inline errno_t futex_down(futex_t *futex)
     183{
     184        return futex_down_timeout(futex, NULL);
     185}
     186
    134187#endif
    135188
Note: See TracChangeset for help on using the changeset viewer.