| 1 | /* @title: Mutex */ |
| 2 | #pragma once |
| 3 | #include <stdbool.h> |
| 4 | #include <stdint.h> |
| 5 | #include <sync/lock_chk_types.h> |
| 6 | |
| 7 | /* mutex: pointer sized mutex |
| 8 | * |
| 9 | * ┌─────────────────────────┐ |
| 10 | * Bits │ .... .... .... 3..0 │ |
| 11 | * Use │ %%%% %%%% %%%% %rrh │ |
| 12 | * └─────────────────────────┘ |
| 13 | * |
| 14 | * h - "held" - is the lock held? |
| 15 | * |
| 16 | * r - reserved for future use |
| 17 | * |
| 18 | * %%%% - pointer to owner thread |
| 19 | * |
| 20 | */ |
| 21 | |
| 22 | struct mutex { |
| 23 | _Atomic(uintptr_t) lock_word; |
| 24 | |
| 25 | #ifdef DEBUG_LOCK_CHK |
| 26 | struct lock_chk_lock chk; |
| 27 | #endif /* DEBUG_LOCK_CHK */ |
| 28 | }; |
| 29 | |
| 30 | void mutex_init_chk_internal(struct mutex *mtx, |
| 31 | const struct lock_chk_class *class, |
| 32 | enum lock_chk_flags flags); |
| 33 | void mutex_set_chk_flags(struct mutex *mtx, enum lock_chk_flags flags); |
| 34 | void mutex_reinit_chk(struct mutex *mtx, const struct lock_chk_class *class, |
| 35 | enum lock_chk_flags flags); |
| 36 | void mutex_unlock_internal(struct mutex *mutex, |
| 37 | const struct lock_chk_site *site); |
| 38 | void mutex_lock_internal(struct mutex *mutex, const struct lock_chk_site *site); |
| 39 | void mutex_lock_subclass_internal(struct mutex *mutex, uint8_t subclass, |
| 40 | const struct lock_chk_site *site); |
| 41 | bool mutex_locked(struct mutex *mtx); |
| 42 | struct thread *mutex_get_owner(struct mutex *mtx); |
| 43 | void mutex_assert_held_internal(struct mutex *mtx, |
| 44 | const struct lock_chk_site *site); |
| 45 | void mutex_assert_not_held_internal(struct mutex *mtx, |
| 46 | const struct lock_chk_site *site); |
| 47 | |
| 48 | #ifdef DEBUG_LOCK_CHK |
| 49 | |
| 50 | #define MUTEX_INIT_CHK(class_, flags_) \ |
| 51 | ((struct mutex) { \ |
| 52 | .lock_word = ATOMIC_VAR_INIT(0), \ |
| 53 | .chk = LOCK_CHK_LOCK_VALUE_INIT((class_), (flags_)), \ |
| 54 | }) |
| 55 | |
| 56 | #define mutex_init_chk(mtx_, class_, flags_) \ |
| 57 | mutex_init_chk_internal((mtx_), (class_), (flags_)) |
| 58 | #define mutex_init_auto_internal(mtx_, flags_) \ |
| 59 | do { \ |
| 60 | static const struct lock_chk_class __auto_class = { \ |
| 61 | .name = #mtx_, \ |
| 62 | .file = __RELFILE__, \ |
| 63 | .line = __LINE__, \ |
| 64 | }; \ |
| 65 | mutex_init_chk_internal((mtx_), &__auto_class, (flags_)); \ |
| 66 | } while (0) |
| 67 | |
| 68 | #else /* !defined(DEBUG_LOCK_CHK) */ |
| 69 | |
| 70 | #define MUTEX_INIT_CHK(class_, flags_) \ |
| 71 | ((struct mutex) {.lock_word = ATOMIC_VAR_INIT(0)}) |
| 72 | |
| 73 | #define mutex_init_chk(mtx_, class_, flags_) \ |
| 74 | mutex_init_chk_internal((mtx_), NULL, LOCK_UNCHKD) |
| 75 | #define mutex_init_auto_internal(mtx_, flags_) \ |
| 76 | mutex_init_chk_internal((mtx_), NULL, LOCK_UNCHKD) |
| 77 | |
| 78 | #endif /* DEBUG_LOCK_CHK */ |
| 79 | |
| 80 | #define MUTEX_INIT MUTEX_INIT_CHK(NULL, LOCK_CHKD_FULL) |
| 81 | #define MUTEX_DEFINE(id) struct mutex id = MUTEX_INIT |
| 82 | #define MUTEX_DEFINE_CHK(id, class_, flags_) \ |
| 83 | struct mutex id = MUTEX_INIT_CHK((class_), (flags_)) |
| 84 | |
| 85 | #define mutex_init_1(mtx_) mutex_init_auto_internal((mtx_), LOCK_CHKD_FULL) |
| 86 | #define mutex_init_2(mtx_, flags_) mutex_init_auto_internal((mtx_), (flags_)) |
| 87 | #define mutex_init(...) _DISPATCH(mutex_init, PP_NARG(__VA_ARGS__))(__VA_ARGS__) |
| 88 | |
| 89 | #define mutex_lock(mutex_) mutex_lock_internal((mutex_), LOCK_CHK_SITE_HERE()) |
| 90 | #define mutex_lock_subclass(mutex_, subclass_) \ |
| 91 | mutex_lock_subclass_internal((mutex_), (subclass_), LOCK_CHK_SITE_HERE()) |
| 92 | #define mutex_unlock(mutex_) \ |
| 93 | mutex_unlock_internal((mutex_), LOCK_CHK_SITE_HERE()) |
| 94 | |
| 95 | #define MUTEX_ASSERT_HELD(m) \ |
| 96 | mutex_assert_held_internal((m), LOCK_CHK_SITE_HERE()) |
| 97 | #define MUTEX_ASSERT_NOT_HELD(m) \ |
| 98 | mutex_assert_not_held_internal((m), LOCK_CHK_SITE_HERE()) |
| 99 | |