mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-13 02:36:59 +02:00
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.forissier@linaro.org>
166 lines
3.2 KiB
C
166 lines
3.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2021 Ahmad Fatoum, Pengutronix
|
|
* Copyright (C) 2025 Linaro Limited
|
|
*
|
|
* An implementation of cooperative multi-tasking inspired from barebox threads
|
|
* https://github.com/barebox/barebox/blob/master/common/bthread.c
|
|
*/
|
|
|
|
#include <compiler.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/list.h>
|
|
#include <malloc.h>
|
|
#include <setjmp.h>
|
|
#include <stdint.h>
|
|
#include <uthread.h>
|
|
|
|
static struct uthread main_thread = {
|
|
.list = LIST_HEAD_INIT(main_thread.list),
|
|
};
|
|
|
|
static struct uthread *current = &main_thread;
|
|
|
|
/**
|
|
* uthread_trampoline() - Call the current thread's entry point then resume the
|
|
* main thread.
|
|
*
|
|
* This is a helper function which is used as the @func argument to the
|
|
* initjmp() function, and ultimately invoked via setjmp(). It does not return
|
|
* but instead longjmp()'s back to the main thread.
|
|
*/
|
|
static void __noreturn uthread_trampoline(void)
|
|
{
|
|
struct uthread *curr = current;
|
|
|
|
curr->fn(curr->arg);
|
|
curr->done = true;
|
|
current = &main_thread;
|
|
longjmp(current->ctx, 1);
|
|
/* Not reached */
|
|
while (true)
|
|
;
|
|
}
|
|
|
|
/**
|
|
* uthread_free() - Free memory used by a uthread object.
|
|
*/
|
|
static void uthread_free(struct uthread *uthread)
|
|
{
|
|
if (!uthread)
|
|
return;
|
|
free(uthread->stack);
|
|
free(uthread);
|
|
}
|
|
|
|
int uthread_create(struct uthread *uthr, void (*fn)(void *), void *arg,
|
|
size_t stack_sz, unsigned int grp_id)
|
|
{
|
|
bool user_allocated = false;
|
|
|
|
if (!stack_sz)
|
|
stack_sz = CONFIG_UTHREAD_STACK_SIZE;
|
|
|
|
if (uthr) {
|
|
user_allocated = true;
|
|
} else {
|
|
uthr = calloc(1, sizeof(*uthr));
|
|
if (!uthr)
|
|
return -1;
|
|
}
|
|
|
|
uthr->stack = memalign(16, stack_sz);
|
|
if (!uthr->stack)
|
|
goto err;
|
|
|
|
uthr->fn = fn;
|
|
uthr->arg = arg;
|
|
uthr->grp_id = grp_id;
|
|
|
|
list_add_tail(&uthr->list, ¤t->list);
|
|
|
|
initjmp(uthr->ctx, uthread_trampoline, uthr->stack, stack_sz);
|
|
|
|
return 0;
|
|
err:
|
|
if (!user_allocated)
|
|
free(uthr);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* uthread_resume() - switch execution to a given thread
|
|
*
|
|
* @uthread: the thread object that should be resumed
|
|
*/
|
|
static void uthread_resume(struct uthread *uthread)
|
|
{
|
|
if (!setjmp(current->ctx)) {
|
|
current = uthread;
|
|
longjmp(uthread->ctx, 1);
|
|
}
|
|
}
|
|
|
|
bool uthread_schedule(void)
|
|
{
|
|
struct uthread *next;
|
|
struct uthread *tmp;
|
|
|
|
list_for_each_entry_safe(next, tmp, ¤t->list, list) {
|
|
if (!next->done) {
|
|
uthread_resume(next);
|
|
return true;
|
|
}
|
|
/* Found a 'done' thread, free its resources */
|
|
list_del(&next->list);
|
|
uthread_free(next);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unsigned int uthread_grp_new_id(void)
|
|
{
|
|
static unsigned int id;
|
|
|
|
return ++id;
|
|
}
|
|
|
|
bool uthread_grp_done(unsigned int grp_id)
|
|
{
|
|
struct uthread *next;
|
|
|
|
list_for_each_entry(next, &main_thread.list, list) {
|
|
if (next->grp_id == grp_id && !next->done)
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|