| #pragma once | |
| #include <cdefs.h> | |
| #include <stdatomic.h> | |
| #include <stdint.h> | |
| #include <thread.h> | |
| #include <timeout.h> | |
| /** | |
| * State for a flag lock. Flag locks use a single futex word to store the lock | |
| * state. | |
| */ | |
| struct FlagLockState | |
| { | |
| /** | |
| * The lock word. One bit is used to indicate that the lock is held, | |
| * another to indicate that the lock has waiters. In priority-inheriting | |
| * locks, the low 16 bits store the ID of the thread that currently holds | |
| * the lock. | |
| */ | |
| _Atomic(uint32_t) lockWord __if_cxx(= 0); | |
| }; | |
| /** | |
| * Ticket locks use two monotonic counters to store the lock state. | |
| */ | |
| struct TicketLockState | |
| { | |
| /** | |
| * The value for the current ticket holder. | |
| */ | |
| _Atomic(uint32_t) current __if_cxx(= 0); | |
| /** | |
| * The value for the next ticket to be granted. | |
| */ | |
| _Atomic(uint32_t) next __if_cxx(= 0); | |
| }; | |
| /** | |
| * State for a recursive mutex. Recursive mutexes allow a single thread to | |
| * acquire the lock multiple times. | |
| */ | |
| struct RecursiveMutexState | |
| { | |
| /** | |
| * The underlying lock. | |
| */ | |
| struct FlagLockState lock; | |
| /** | |
| * The count of the times the lock has been acquired by the same thread. | |
| * This must be initialised to 0. | |
| */ | |
| uint16_t depth __if_cxx(= 0); | |
| }; | |
| /** | |
| * State for a counting semaphore. | |
| */ | |
| struct CountingSemaphoreState | |
| { | |
| /** | |
| * The current counter value. | |
| */ | |
| _Atomic(uint32_t) count; | |
| /** | |
| * The maximum value for the counter. | |
| */ | |
| uint32_t maxCount; | |
| }; | |
| __BEGIN_DECLS | |
| /** | |
| * Try to lock a flag lock. This is the non-priority-inheriting version, | |
| * sometimes called a binary semaphore on other platforms. | |
| * | |
| * Returns 0 on success, -ETIMEDOUT if the timeout expired, -EINVAL if the | |
| * arguments are invalid, or -ENOENT if the lock is set in destruction mode. | |
| */ | |
| int __cheri_libcall flaglock_trylock(Timeout *timeout, | |
| struct FlagLockState *lock); | |
| /** | |
| * Try to lock a flag lock. This is the priority-inheriting version. Some | |
| * other platforms refer to this as a priority-inheriting mutex or simply a | |
| * mutex. | |
| * | |
| * A higher-priority thread that calls this function while a lock is held will | |
| * lend its priority to the thread that successfully called this function until | |
| * that thread either releases the lock with `flaglock_unlock` or the timeout | |
| * expires. | |
| * | |
| * Returns 0 on success, -ETIMEDOUT if the timeout expired, -EINVAL if the | |
| * arguments are invalid, or -ENOENT if the lock is set in destruction mode. | |
| * | |
| * Note: if the object is deallocated while trying to acquire the lock, then | |
| * this will fault. In many cases, this is called at a compartment boundary | |
| * and so this is fine. If it is not acceptable, use `heap_claim_fast` to | |
| * ensure that the object remains live until after the call. | |
| */ | |
| int __cheri_libcall | |
| flaglock_priority_inheriting_trylock(Timeout *timeout, | |
| struct FlagLockState *lock); | |
| /** | |
| * Convenience wrapper to acquire a lock with an unlimited timeout. See | |
| * `flaglock_trylock` for more details. | |
| */ | |
| __always_inline static inline void flaglock_lock(struct FlagLockState *lock) | |
| { | |
| Timeout t = {0, UnlimitedTimeout}; | |
| flaglock_trylock(&t, lock); | |
| } | |
| /** | |
| * Convenience wrapper to acquire a lock with an unlimited timeout. See | |
| * `flaglock_priority_inheriting_trylock` for more details. | |
| */ | |
| __always_inline static inline void | |
| flaglock_priority_inheriting_lock(struct FlagLockState *lock) | |
| { | |
| Timeout t = {0, UnlimitedTimeout}; | |
| flaglock_priority_inheriting_trylock(&t, lock); | |
| } | |
| /** | |
| * Unlock a flag lock. This can be called with either form of flag lock. | |
| */ | |
| void __cheri_libcall flaglock_unlock(struct FlagLockState *lock); | |
| /** | |
| * Set a flag lock in destruction mode. | |
| * | |
| * Locks in destruction mode cannot be acquired by other threads. Any threads | |
| * currently attempting to acquire the lock will wake and fail to acquire the | |
| * lock. This should be called before deallocating an object that contains a | |
| * lock. | |
| * | |
| * Note that callers do not need to hold the lock; the ability to upgrade for | |
| * destruction without holding the lock is useful for cleaning up from the | |
| * error handler. | |
| */ | |
| void __cheri_libcall | |
| flaglock_upgrade_for_destruction(struct FlagLockState *lock); | |
| /** | |
| * Return the thread ID of the owner of the lock. | |
| * | |
| * This is only available for priority inherited locks, as this is the only | |
| * case where we store the thread ID of the owner. | |
| * | |
| * The return value is 0 if the lock is not owned or if called on a | |
| * non-priority inherited flag lock. The return value is undefined if called on | |
| * an uninitialized lock. | |
| * | |
| * This *will* race with succesful `lock` and `unlock` operations on other | |
| * threads, and should thus not be used to check if the lock is owned. | |
| * | |
| * The main use case for this function is in the error handler to check whether | |
| * or not the lock is owned by the thread on which the error handler was | |
| * invoked. In this case we can call this function and compare the result with | |
| * `thread_id_get` to know if the current thread owns the lock. | |
| */ | |
| __always_inline static inline uint16_t | |
| flaglock_priority_inheriting_get_owner_thread_id(struct FlagLockState *lock) | |
| { | |
| // The lock must be held at this point for the value to be stable so do | |
| // a non-atomic read (simply &ing the lock word would result in a | |
| // libcall for the atomic operation). | |
| return ((*(uint32_t *)&(lock->lockWord)) & 0x0000ffff); | |
| } | |
| /** | |
| * Try to acquire a recursive mutex. This is a priority-inheriting mutex that | |
| * can be acquired multiple times by the same thread. | |
| * | |
| * A higher-priority thread that calls this function while a lock is held will | |
| * lend its priority to the thread that successfully called this function until | |
| * that thread either releases the lock with `flaglock_unlock` or the timeout | |
| * expires. | |
| * | |
| * Returns 0 on success, -ETIMEDOUT if the timeout expired, or -EINVAL if the | |
| * arguments are invalid. Can also return -EOVERFLOW if the lock depth would | |
| * overflow the depth counter. | |
| */ | |
| int __cheri_libcall recursivemutex_trylock(Timeout *timeout, | |
| struct RecursiveMutexState *lock); | |
| /** | |
| * Unlock a recursive mutex. Note: This does not check that the current thread | |
| * holds the lock. An error-checking mutex can be implemented by wrapping this | |
| * in a check that the `owner` field matches the current thread ID. | |
| * | |
| * Returns 0 on success. Succeeds unconditionally (future versions may return | |
| * non-zero on error). | |
| */ | |
| int __cheri_libcall recursivemutex_unlock(struct RecursiveMutexState *mutex); | |
| /** | |
| * Acquire a ticket lock. Ticket locks, by design, cannot support a try-lock | |
| * operation and so will block forever until the lock is acquired. | |
| */ | |
| void __cheri_libcall ticketlock_lock(struct TicketLockState *lock); | |
| /** | |
| * Release a ticket lock. | |
| */ | |
| void __cheri_libcall ticketlock_unlock(struct TicketLockState *lock); | |
| /** | |
| * Semaphore get operation, decrements the semaphore count. Returns 0 on | |
| * success, -ETIMEDOUT if the timeout expired. Can also return -EINVAL if the | |
| * arguments are invalid. | |
| */ | |
| int __cheri_libcall semaphore_get(Timeout *timeout, | |
| struct CountingSemaphoreState *semaphore); | |
| /** | |
| * Semaphore put operation. Returns 0 on success, -EINVAL if this would push | |
| * the semaphore count above the maximum. | |
| */ | |
| int __cheri_libcall semaphore_put(struct CountingSemaphoreState *semaphore); | |
| __END_DECLS |