Add struct uthread_mutex and uthread_mutex_lock(), uthread_mutex_trylock(), uthread_mutex_unlock() to protect shared data structures from concurrent modifications.
Signed-off-by: Jerome Forissier <jerome.foriss...@linaro.org> --- Changes in v6: - uthread_mutex_trylock(): return -EBUSY instead of EBUSY include/uthread.h | 60 +++++++++++++++++++++++++++++++++++++++++++++++ lib/uthread.c | 27 +++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/include/uthread.h b/include/uthread.h index f796a16f25f..89fa552a6f6 100644 --- a/include/uthread.h +++ b/include/uthread.h @@ -50,6 +50,23 @@ struct uthread { struct list_head list; }; +/** + * Internal state of a struct uthread_mutex + */ +enum uthread_mutex_state { + UTHREAD_MUTEX_UNLOCKED = 0, + UTHREAD_MUTEX_LOCKED = 1 +}; + +/** + * Uthread mutex + */ +struct uthread_mutex { + enum uthread_mutex_state state; +}; + +#define UTHREAD_MUTEX_INITIALIZER { .state = UTHREAD_MUTEX_UNLOCKED } + #ifdef CONFIG_UTHREAD /** @@ -94,6 +111,44 @@ unsigned int uthread_grp_new_id(void); */ bool uthread_grp_done(unsigned int grp_id); +/** + * uthread_mutex_lock() - lock a mutex + * + * If the cwmutexlock is available (i.e., not owned by any other thread), then + * it is locked for use by the current thread. Otherwise the current thread + * blocks: it enters a wait loop by scheduling other threads until the mutex + * becomes unlocked. + * + * @mutex: pointer to the mutex to lock + * Return: 0 on success, in which case the lock is owned by the calling thread. + * != 0 otherwise (the lock is not owned by the calling thread). + */ +int uthread_mutex_lock(struct uthread_mutex *mutex); + +/** + * uthread_mutex_trylock() - lock a mutex if not currently locked + * + * Similar to uthread_mutex_lock() except return immediately if the mutex is + * locked already. + * + * @mutex: pointer to the mutex to lock + * Return: 0 on success, in which case the lock is owned by the calling thread. + * EBUSY if the mutex is already locked by another thread. Any other non-zero + * value on error. + */ +int uthread_mutex_trylock(struct uthread_mutex *mutex); + +/** + * uthread_mutex_unlock() - unlock a mutex + * + * The mutex is assumed to be owned by the calling thread on entry. On exit, it + * is unlocked. + * + * @mutex: pointer to the mutex to unlock + * Return: 0 on success, != 0 on error + */ +int uthread_mutex_unlock(struct uthread_mutex *mutex); + #else static inline int uthread_create(struct uthread *uthr, void (*fn)(void *), @@ -119,5 +174,10 @@ static inline bool uthread_grp_done(unsigned int grp_id) return true; } +/* These are macros for convenience on the caller side */ +#define uthread_mutex_lock(_mutex) ({ 0; }) +#define uthread_mutex_trylock(_mutex) ({ 0 }) +#define uthread_mutex_unlock(_mutex) ({ 0; }) + #endif /* CONFIG_UTHREAD */ #endif /* _UTHREAD_H_ */ diff --git a/lib/uthread.c b/lib/uthread.c index aa264b1d95f..062fca7d209 100644 --- a/lib/uthread.c +++ b/lib/uthread.c @@ -8,6 +8,7 @@ */ #include <compiler.h> +#include <linux/errno.h> #include <linux/kernel.h> #include <linux/list.h> #include <malloc.h> @@ -136,3 +137,29 @@ bool uthread_grp_done(unsigned int grp_id) return true; } + +int uthread_mutex_lock(struct uthread_mutex *mutex) +{ + while (mutex->state == UTHREAD_MUTEX_LOCKED) + uthread_schedule(); + + mutex->state = UTHREAD_MUTEX_LOCKED; + return 0; +} + +int uthread_mutex_trylock(struct uthread_mutex *mutex) +{ + if (mutex->state == UTHREAD_MUTEX_UNLOCKED) { + mutex->state = UTHREAD_MUTEX_LOCKED; + return 0; + } + + return -EBUSY; +} + +int uthread_mutex_unlock(struct uthread_mutex *mutex) +{ + mutex->state = UTHREAD_MUTEX_UNLOCKED; + + return 0; +} -- 2.43.0