Ignore:
File:
1 edited

Legend:

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

    ra35b458 rb59318e  
    3636#define LIBC_FUTEX_H_
    3737
     38#include <assert.h>
    3839#include <atomic.h>
    3940#include <errno.h>
    4041#include <libc.h>
     42#include <time.h>
    4143
    4244typedef struct futex {
    4345        atomic_t val;
    44 #ifdef FUTEX_UPGRADABLE
    45         int upgraded;
     46#ifdef CONFIG_DEBUG_FUTEX
     47        _Atomic void *owner;
    4648#endif
    4749} futex_t;
    4850
    49 
    5051extern void futex_initialize(futex_t *futex, int value);
    5152
    52 #ifdef FUTEX_UPGRADABLE
    53 #include <rcu.h>
     53#ifdef CONFIG_DEBUG_FUTEX
    5454
    55 #define FUTEX_INITIALIZE(val) {{ (val) }, 0}
     55#define FUTEX_INITIALIZE(val) {{ (val) }, NULL }
     56#define FUTEX_INITIALIZER     FUTEX_INITIALIZE(1)
    5657
    57 #define futex_lock(fut) \
    58 ({ \
    59         rcu_read_lock(); \
    60         (fut)->upgraded = rcu_access(_upgrade_futexes); \
    61         if ((fut)->upgraded) \
    62                 (void) futex_down((fut)); \
    63 })
     58void __futex_assert_is_locked(futex_t *, const char *);
     59void __futex_assert_is_not_locked(futex_t *, const char *);
     60void __futex_lock(futex_t *, const char *);
     61void __futex_unlock(futex_t *, const char *);
     62bool __futex_trylock(futex_t *, const char *);
     63void __futex_give_to(futex_t *, void *, const char *);
    6464
    65 #define futex_trylock(fut) \
    66 ({ \
    67         rcu_read_lock(); \
    68         int _upgraded = rcu_access(_upgrade_futexes); \
    69         if (_upgraded) { \
    70                 int _acquired = futex_trydown((fut)); \
    71                 if (!_acquired) { \
    72                         rcu_read_unlock(); \
    73                 } else { \
    74                         (fut)->upgraded = true; \
    75                 } \
    76                 _acquired; \
    77         } else { \
    78                 (fut)->upgraded = false; \
    79                 1; \
    80         } \
    81 })
     65#define futex_lock(futex) __futex_lock((futex), #futex)
     66#define futex_unlock(futex) __futex_unlock((futex), #futex)
     67#define futex_trylock(futex) __futex_trylock((futex), #futex)
    8268
    83 #define futex_unlock(fut) \
    84 ({ \
    85         if ((fut)->upgraded) \
    86                 (void) futex_up((fut)); \
    87         rcu_read_unlock(); \
    88 })
    89 
    90 extern int _upgrade_futexes;
    91 
    92 extern void futex_upgrade_all_and_wait(void);
     69#define futex_give_to(futex, new_owner) __futex_give_to((futex), (new_owner), #futex)
     70#define futex_assert_is_locked(futex) __futex_assert_is_locked((futex), #futex)
     71#define futex_assert_is_not_locked(futex) __futex_assert_is_not_locked((futex), #futex)
    9372
    9473#else
    9574
    9675#define FUTEX_INITIALIZE(val) {{ (val) }}
     76#define FUTEX_INITIALIZER     FUTEX_INITIALIZE(1)
    9777
    9878#define futex_lock(fut)     (void) futex_down((fut))
     
    10080#define futex_unlock(fut)   (void) futex_up((fut))
    10181
     82#define futex_give_to(fut, owner) ((void)0)
     83#define futex_assert_is_locked(fut) assert((atomic_signed_t) (fut)->val.count <= 0)
     84#define futex_assert_is_not_locked(fut) ((void)0)
     85
    10286#endif
    103 
    104 #define FUTEX_INITIALIZER     FUTEX_INITIALIZE(1)
    10587
    10688/** Try to down the futex.
     
    11799}
    118100
    119 /** 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.
    120108 *
    121109 * @param futex Futex.
    122110 *
    123111 * @return ENOENT if there is no such virtual address.
     112 * @return ETIMEOUT if timeout expires.
    124113 * @return EOK on success.
    125114 * @return Error code from <errno.h> otherwise.
    126115 *
    127116 */
    128 static inline errno_t futex_down(futex_t *futex)
     117static inline errno_t futex_down_composable(futex_t *futex, struct timeval *expires)
    129118{
     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
    130137        if ((atomic_signed_t) atomic_predec(&futex->val) < 0)
    131                 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);
    132139
    133140        return EOK;
     
    151158}
    152159
     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
    153187#endif
    154188
Note: See TracChangeset for help on using the changeset viewer.