Changeset 710c1e9 in mainline


Ignore:
Timestamp:
2018-07-13T16:54:22Z (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:
868d75c
Parents:
e3787a0
git-author:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-07-01 17:23:12)
git-committer:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-07-13 16:54:22)
Message:

More robust non-blocking futex down.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/include/futex.h

    re3787a0 r710c1e9  
    8686#endif
    8787
    88 /** Try to down the futex.
    89  *
    90  * @param futex Futex.
    91  *
    92  * @return true if the futex was acquired.
    93  * @return false if the futex was not acquired.
    94  *
    95  */
    96 static inline bool futex_trydown(futex_t *futex)
    97 {
    98         return cas(&futex->val, 1, 0);
    99 }
    100 
    10188/** Down the futex with timeout, composably.
    10289 *
     
    115102 *
    116103 */
    117 static inline errno_t futex_down_composable(futex_t *futex, struct timeval *expires)
     104static inline errno_t futex_down_composable(futex_t *futex, const struct timeval *expires)
    118105{
    119106        // TODO: Add tests for this.
    120107
    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)) {
     108        if ((atomic_signed_t) atomic_predec(&futex->val) >= 0)
     109                return EOK;
     110
     111        suseconds_t timeout;
     112
     113        if (!expires) {
     114                /* No timeout. */
     115                timeout = 0;
     116        } else {
     117                if (expires->tv_sec == 0) {
    128118                        /* We can't just return ETIMEOUT. That wouldn't be composable. */
    129119                        timeout = 1;
    130120                } else {
    131                         timeout = tv_sub_diff(expires, &tv);
     121                        struct timeval tv;
     122                        getuptime(&tv);
     123                        timeout = tv_gteq(&tv, expires) ? 1 :
     124                            tv_sub_diff(expires, &tv);
    132125                }
    133126
     
    135128        }
    136129
    137         if ((atomic_signed_t) atomic_predec(&futex->val) < 0)
    138                 return (errno_t) __SYSCALL2(SYS_FUTEX_SLEEP, (sysarg_t) &futex->val.count, (sysarg_t) timeout);
    139 
    140         return EOK;
     130        return __SYSCALL2(SYS_FUTEX_SLEEP, (sysarg_t) &futex->val.count, (sysarg_t) timeout);
    141131}
    142132
     
    153143{
    154144        if ((atomic_signed_t) atomic_postinc(&futex->val) < 0)
    155                 return (errno_t) __SYSCALL1(SYS_FUTEX_WAKEUP, (sysarg_t) &futex->val.count);
     145                return __SYSCALL1(SYS_FUTEX_WAKEUP, (sysarg_t) &futex->val.count);
    156146
    157147        return EOK;
    158148}
    159149
    160 static inline errno_t futex_down_timeout(futex_t *futex, struct timeval *expires)
    161 {
     150static inline errno_t futex_down_timeout(futex_t *futex, const struct timeval *expires)
     151{
     152        if (expires && expires->tv_sec == 0 && expires->tv_usec == 0) {
     153                /* Nonblocking down. */
     154
     155                /*
     156                 * Try good old CAS a few times.
     157                 * Not too much though, we don't want to bloat the caller.
     158                 */
     159                for (int i = 0; i < 2; i++) {
     160                        atomic_signed_t old = atomic_get(&futex->val);
     161                        if (old <= 0)
     162                                return ETIMEOUT;
     163
     164                        if (cas(&futex->val, old, old - 1))
     165                                return EOK;
     166                }
     167
     168                // TODO: builtin atomics with relaxed ordering can make this
     169                //       faster.
     170
     171                /*
     172                 * If we don't succeed with CAS, we can't just return failure
     173                 * because that would lead to spurious failures where
     174                 * futex_down_timeout returns ETIMEOUT despite there being
     175                 * available tokens. That could break some algorithms.
     176                 * We also don't want to loop on CAS indefinitely, because
     177                 * that would make the semaphore not wait-free, even when all
     178                 * atomic operations and the underlying base semaphore are all
     179                 * wait-free.
     180                 * Instead, we fall back to regular down_timeout(), with
     181                 * an already expired deadline. That way we delegate all these
     182                 * concerns to the base semaphore.
     183                 */
     184        }
     185
    162186        /*
    163187         * This combination of a "composable" sleep followed by futex_up() on
     
    171195}
    172196
     197/** Try to down the futex.
     198 *
     199 * @param futex Futex.
     200 *
     201 * @return true if the futex was acquired.
     202 * @return false if the futex was not acquired.
     203 *
     204 */
     205static inline bool futex_trydown(futex_t *futex)
     206{
     207        /*
     208         * down_timeout with an already expired deadline should behave like
     209         * trydown.
     210         */
     211        struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };
     212        return futex_down_timeout(futex, &tv) == EOK;
     213}
     214
    173215/** Down the futex.
    174216 *
Note: See TracChangeset for help on using the changeset viewer.