You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
3.9 KiB
C
169 lines
3.9 KiB
C
/* See the end of this file for copyright, license, and warranty information. */
|
|
|
|
#pragma once
|
|
|
|
#include <ardix/atomic.h>
|
|
#include <ardix/list.h>
|
|
#include <ardix/types.h>
|
|
|
|
#include <errno.h>
|
|
#include <toolchain.h>
|
|
|
|
/**
|
|
* @defgroup mutex Synchronization Primitives
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief A spinning mutex.
|
|
* This is the most primitive type of mutex. It is locked by looping (spinning)
|
|
* so long until the lock could be acquired, thus blocking the CPU from doing
|
|
* anything else. For this reason, tasks that hold a spinlock must not sleep
|
|
* while holding such a lock. This behavior is enforced by calling
|
|
* `atomic_enter()` in the `spin_lock()` function and `atomic_leave()` in
|
|
* `spin_unlock()`.
|
|
*/
|
|
typedef struct {
|
|
uint8_t lock;
|
|
} spin_t;
|
|
|
|
extern void _spin_lock(uint8_t *lock);
|
|
extern void _spin_unlock(uint8_t *lock);
|
|
extern int _spin_trylock(uint8_t *lock);
|
|
|
|
__always_inline void spin_init(spin_t *spin)
|
|
{
|
|
spin->lock = 0;
|
|
}
|
|
|
|
__always_inline void spin_lock(spin_t *spin)
|
|
{
|
|
atomic_enter();
|
|
_spin_lock(&spin->lock);
|
|
}
|
|
|
|
__always_inline void spin_unlock(spin_t *spin)
|
|
{
|
|
_spin_unlock(&spin->lock);
|
|
atomic_leave();
|
|
}
|
|
|
|
__always_inline int spin_trylock(spin_t *spin)
|
|
{
|
|
if (_spin_trylock(&spin->lock) == 0)
|
|
return 0;
|
|
else
|
|
return -EAGAIN;
|
|
}
|
|
|
|
__always_inline bool spin_is_locked(spin_t *spin)
|
|
{
|
|
return spin->lock != 0;
|
|
}
|
|
|
|
/**
|
|
* @brief A simple mutex.
|
|
*
|
|
* Mutexes can be locked and unlocked using the `mutex_lock()` and
|
|
* `mutex_unlock()` methods respectively. The former will block until the lock
|
|
* is acquired and thus should never be used from interrupt context.
|
|
* Use `mutex_trylock()` if you don't want blocking.
|
|
*/
|
|
struct mutex {
|
|
uint8_t lock; /**< Current lock value, don't read directly */
|
|
spin_t wait_queue_lock;
|
|
struct list_head wait_queue; /**< -> mutex_wait::link */
|
|
};
|
|
|
|
struct mutex_wait {
|
|
struct list_head link;
|
|
struct task *task;
|
|
};
|
|
|
|
/**
|
|
* @brief Internal assembly routine for `mutex_trylock()`.
|
|
* @private
|
|
*/
|
|
extern int _mutex_trylock(uint8_t *lock);
|
|
/**
|
|
* @brief Internal assembly routine for `mutex_unlock()`.
|
|
* @private
|
|
*/
|
|
extern void _mutex_unlock(uint8_t *lock);
|
|
|
|
/**
|
|
* @brief Initialize a mutex and set it to unlocked.
|
|
*
|
|
* @param mutex Mutex to initialize
|
|
*/
|
|
void mutex_init(struct mutex *mutex);
|
|
|
|
/**
|
|
* @brief Acquire an exclusive lock on a mutex.
|
|
* This call may sleep if the lock cannot be acquired instantly.
|
|
*
|
|
* @param mutex Mutex to lock
|
|
*/
|
|
void mutex_lock(struct mutex *mutex);
|
|
|
|
/**
|
|
* @brief Release an exclusive lock on a mutex.
|
|
*
|
|
* @param mutex Mutex to unlock
|
|
*/
|
|
void mutex_unlock(struct mutex *mutex);
|
|
|
|
/**
|
|
* @brief Attempt to acquire an exclusive lock on a mutex.
|
|
* The return value is zero if the claim succeeds.
|
|
*
|
|
* @param mutex Mutex to attempt to lock
|
|
* @returns 0 if the lock was acquired, `-EAGAIN` otherwise
|
|
*/
|
|
__always_inline int mutex_trylock(struct mutex *mutex)
|
|
{
|
|
if (_mutex_trylock(&mutex->lock) == 0)
|
|
return 0;
|
|
else
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/**
|
|
* @brief Determine whether a mutex is locked.
|
|
*
|
|
* @param mutex Mutex to get the lock value of
|
|
* @returns true if the mutex is locked
|
|
*/
|
|
__always_inline bool mutex_is_locked(struct mutex *mutex)
|
|
{
|
|
return mutex->lock != 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Statically declare and define a mutex (file scope only).
|
|
* The mutex does not need to be initialized using `mutex_init()`.
|
|
*
|
|
* @param name Name of the `struct mutex` that will be defined
|
|
*/
|
|
#define MUTEX(name) struct mutex name = { \
|
|
.lock = 0, \
|
|
.wait_queue_lock = { .lock = 0 }, \
|
|
.wait_queue = LIST_HEAD_INIT(name.wait_queue) \
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/*
|
|
* This file is part of Ardix.
|
|
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.
|
|
*
|
|
* Ardix is non-violent software: you may only use, redistribute,
|
|
* and/or modify it under the terms of the CNPLv6+ as found in
|
|
* the LICENSE file in the source code root directory or at
|
|
* <https://git.pixie.town/thufie/CNPL>.
|
|
*
|
|
* Ardix comes with ABSOLUTELY NO WARRANTY, to the extent
|
|
* permitted by applicable law. See the CNPLv6+ for details.
|
|
*/
|