Files
mr-library/kernel/wait.c
MacRsh 8bfc6c9390 feat(service): Added service macro functions.
1.New macros for obtaining the number of variable parameters, adaptive parameter concatenation macros, and separated concatenation macros have been added (in preparation for the device tree).
2025-10-12 22:19:06 +08:00

436 lines
11 KiB
C

/**
* @copyright (c) 2024-2025, MacRsh
*
* @license SPDX-License-Identifier: Apache-2.0
*
* @date 2024-09-06 MacRsh First version
*/
#include <mr-X/mr_wait.h>
#if defined(MR_USE_WAIT)
#include <mr-X/mr_spinlock.h>
#include <libc/mr_malloc.h>
/* Waitable clazz(dynamic & static) */
static void able_release_dyn(mr_object_t *obj);
static void able_release(mr_object_t *obj);
MR_CLAZZ_DYNAMIC_DEFINE(waitable, able_release_dyn);
MR_CLAZZ_DEFINE(waitable, able_release);
/* Waiter clazz(dynamic & static) */
static void wait_release_dyn(mr_object_t *obj);
static void wait_release(mr_object_t *obj);
MR_CLAZZ_DYNAMIC_DEFINE(waiter, wait_release_dyn);
MR_CLAZZ_DEFINE(waiter, wait_release);
/* Wait lock */
static mr_spinlock_t __lock = MR_SPINLOCK_INIT();
/* Wait event mask definition */
#define WAIT_TYPE_MASK (0x70000000U)
#define WAIT_MASK (0x7fffffffU)
static mr_err_t waitable_init(mr_waitable_t *able, const mr_clazz_t *clazz,
mr_ptr_t check, mr_ptr_t param) {
/* Init waitable */
mr_atomic_init(&able->gen, 0);
mr_list_init(&able->list);
able->check = check;
able->param = param;
able->last = 0;
/* Init object */
return mr_object_init((mr_object_t *)able, clazz);
}
mr_err_t mr_waitable_init(mr_waitable_t *able, mr_waitable_check_t *check,
void *param) {
const mr_clazz_t *clazz = MR_CLAZZ_FIND(waitable);
/* Check parameter */
MR_ASSERT((able != MR_NULL) && (!MR_OBJECT_IS_INITED(able)));
/* Init waitable */
return waitable_init(able, clazz, check, param);
}
mr_waitable_t *mr_waitable_create(mr_waitable_check_t *check, void *param) {
const mr_clazz_t *clazz = MR_CLAZZ_DYNAMIC_FIND(waitable);
mr_waitable_t *able;
mr_err_t ret;
/* Create waitable */
able = mr_malloc(sizeof(mr_waitable_t));
if (!able) {
return MR_NULL;
}
/* Init waitable */
ret = waitable_init(able, clazz, check, param);
if (ret != 0) {
mr_free(able);
return MR_NULL;
}
return able;
}
static void waitable_remove(mr_waitable_t *able) {
mr_waiter_t *waiter;
int mask;
/* Lock */
mask = mr_spinlock_lock_irqsave(&__lock);
/* Clear waiting list */
while (!mr_list_is_empty(&able->list)) {
waiter = MR_LIST_ENTRY(able->list.next, mr_waiter_t, list);
/* Remove waiter from waiting list */
mr_list_del(&waiter->list);
}
/* Unlock */
mr_spinlock_unlock_irqrestore(&__lock, mask);
}
mr_err_t mr_waitable_del(mr_waitable_t *able) {
/* Check parameter */
MR_ASSERT((able != MR_NULL) && MR_OBJECT_IS_INITED(able));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(able, waitable));
/* Remove waitable */
waitable_remove(able);
/* Delete object */
mr_object_del((mr_object_t *)able);
return 0;
}
static mr_uint32_t waitable_check(mr_waitable_t *able) {
mr_waitable_check_t *check;
mr_uint32_t event;
mr_atomic_t gen;
void *param;
/* Save waitable generation */
gen = mr_atomic_fetch_add(&able->gen, 1);
/* Save waitable check and param */
check = (mr_waitable_check_t *)able->check;
param = (void *)able->param;
/* Call waitable check */
if (check != MR_NULL) {
event = check(able, param);
} else {
/* Not supported */
return 0;
}
/* Check if waitable generation is changed */
if (mr_atomic_load(&able->gen) != (gen + 1)) {
/* If another check occurs during the check process, the last one shall
* prevail */
return 0;
}
/* Return waitable event */
return event & WAIT_MASK;
}
static void waitable_handle(mr_waitable_t *able, mr_uint32_t event) {
mr_uint32_t event_level, event_edge;
mr_waiter_entry_t *entry;
mr_waiter_t *waiter;
mr_list_t oplist;
void *param;
int mask;
/* Check if event is untyped */
if ((event & WAIT_TYPE_MASK) == 0) {
return;
}
/* Init operation list */
mr_list_init(&oplist);
/* Lock */
mask = mr_spinlock_lock_irqsave(&__lock);
/* Get edge and level event */
event_edge = event & ~able->last;
event_level = event;
/* Update waitable last event */
able->last = event;
/* Replace the waiting list with the operation list (to prevent node removal
* in the middle) */
mr_list_replace(&oplist, &able->list);
/* Check operation list */
while (!mr_list_is_empty(&oplist)) {
waiter = MR_LIST_ENTRY(oplist.next, mr_waiter_t, list);
/* Move waiter to waiting list */
mr_list_move(&waiter->list, &able->list);
/* Check if waiter is triggered */
event = (waiter->event & MR_WAIT_EDGE) ? event_edge : event_level;
if (!(waiter->event & event)) {
/* Not triggered */
continue;
}
/* Save waiter entry and param */
entry = (mr_waiter_entry_t *)waiter->entry;
param = (void *)waiter->param;
/* Unlock */
mr_spinlock_unlock_irqrestore(&__lock, mask);
/* Call waiter entry */
entry(waiter, param, event);
/* Lock */
mask = mr_spinlock_lock_irqsave(&__lock);
}
/* Unlock */
mr_spinlock_unlock_irqrestore(&__lock, mask);
}
mr_err_t mr_waitable_complete(mr_waitable_t *able) {
mr_uint32_t event;
/* Check parameter */
MR_ASSERT((able != MR_NULL) && MR_OBJECT_IS_INITED(able));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(able, waitable));
/* Reduce unnecessary check operations */
if (mr_list_is_empty(&able->list)) {
return 0;
}
/* Get waitable event */
event = waitable_check(able);
/* Handle waitable event */
waitable_handle(able, event);
return 0;
}
mr_err_t mr_waitable_raise(mr_waitable_t *able, mr_uint32_t event) {
/* Check parameter */
MR_ASSERT((able != MR_NULL) && MR_OBJECT_IS_INITED(able));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(able, waitable));
/* Raise waitable event */
waitable_handle(able, event);
return 0;
}
static void able_release(mr_object_t *obj) {
mr_waitable_t *waitable;
/* Get waitable */
waitable = MR_CONTAINER_OF(obj, mr_waitable_t, parent);
/* Remove waitable */
waitable_remove(waitable);
}
static void able_release_dyn(mr_object_t *obj) {
/* Release waitable */
able_release(obj);
/* Free waitable */
mr_free(obj);
}
static mr_err_t waiter_init(mr_waiter_t *wait, const mr_clazz_t *clazz,
mr_ptr_t entry, mr_ptr_t param, mr_uint32_t event) {
/* Init waiter */
mr_list_init(&wait->list);
wait->event = event;
wait->entry = entry;
wait->param = param;
/* Init object */
return mr_object_init((mr_object_t *)wait, clazz);
}
mr_err_t mr_waiter_init(mr_waiter_t *wait, mr_waiter_entry_t *entry,
void *param, mr_uint32_t event) {
const mr_clazz_t *clazz = MR_CLAZZ_FIND(waiter);
/* Check parameter */
MR_ASSERT((wait != MR_NULL) && (!MR_OBJECT_IS_INITED(wait)));
MR_ASSERT((entry != MR_NULL) && ((event & WAIT_TYPE_MASK) != 0));
/* Init waiter */
return waiter_init(wait, clazz, entry, param, event);
}
mr_waiter_t *mr_waiter_create(mr_waiter_entry_t *entry, void *param,
mr_uint32_t event) {
const mr_clazz_t *clazz = MR_CLAZZ_DYNAMIC_FIND(waiter);
mr_waiter_t *wait;
mr_err_t ret;
/* Check parameter */
MR_ASSERT((entry != MR_NULL) && ((event & WAIT_TYPE_MASK) != 0));
/* Create waiter */
wait = mr_malloc(sizeof(mr_waiter_t));
if (!wait) {
return MR_NULL;
}
/* Init waiter */
ret = waiter_init(wait, clazz, entry, param, event);
if (ret != 0) {
mr_free(wait);
return MR_NULL;
}
return wait;
}
static void waiter_remove(mr_waiter_t *wait) {
int mask;
/* Lock */
mask = mr_spinlock_lock_irqsave(&__lock);
/* Remove waiter from waiting list */
mr_list_del(&wait->list);
/* Unlock */
mr_spinlock_unlock_irqrestore(&__lock, mask);
}
mr_err_t mr_waiter_del(mr_waiter_t *wait) {
/* Check parameter */
MR_ASSERT((wait != MR_NULL) && MR_OBJECT_IS_INITED(wait));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(wait, waiter));
/* Remove waiter */
waiter_remove(wait);
/* Delete object */
mr_object_del((mr_object_t *)wait);
return 0;
}
mr_err_t mr_waiter_wait(mr_waiter_t *wait, mr_waitable_t *able) {
mr_waiter_entry_t *entry;
mr_uint32_t event;
mr_err_t ret;
void *param;
int mask;
/* Check parameter */
MR_ASSERT((wait != MR_NULL) && MR_OBJECT_IS_INITED(wait));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(wait, waiter));
MR_ASSERT((able != MR_NULL) && MR_OBJECT_IS_INITED(able));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(able, waitable));
/* Lock */
mask = mr_spinlock_lock_irqsave(&__lock);
/* Check if waiter is waiting */
if (MR_WAITER_IS_WAITING(wait)) {
ret = -MR_EBUSY;
goto _exit;
}
/* Add waiter to waitable waiting list */
mr_list_add(&able->list, &wait->list);
/* Check if waiter is waiting for edge event */
if (wait->event & MR_WAIT_EDGE) {
/* Edge triggering is not related to the previous one */
ret = 0;
goto _exit;
}
/* Unlock */
mr_spinlock_unlock_irqrestore(&__lock, mask);
/* Get waitable event */
event = waitable_check(able);
if ((event & WAIT_TYPE_MASK) == 0) {
/* No waitable event(untyped events will be discarded) */
return 0;
}
/* Check if waiter is triggered */
if (!(wait->event & event)) {
/* Not triggered */
return 0;
}
/* Save waiter entry and param */
entry = (mr_waiter_entry_t *)wait->entry;
param = (void *)wait->param;
/* Call waiter entry */
entry(wait, param, event);
return 0;
_exit:
/* Unlock */
mr_spinlock_unlock_irqrestore(&__lock, mask);
return ret;
}
mr_err_t mr_waiter_cancel(mr_waiter_t *wait) {
/* Check parameter */
MR_ASSERT((wait != MR_NULL) && MR_OBJECT_IS_INITED(wait));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(wait, waiter));
/* Remove waiter */
waiter_remove(wait);
return 0;
}
mr_err_t mr_waiter_event_set(mr_waiter_t *wait, mr_uint32_t event) {
int mask;
/* Check parameter */
MR_ASSERT((wait != MR_NULL) && MR_OBJECT_IS_INITED(wait));
MR_ASSERT(MR_OBJECT_CLAZZ_IS(wait, waiter));
MR_ASSERT((event & WAIT_TYPE_MASK) != 0);
/* Lock */
mask = mr_spinlock_lock_irqsave(&__lock);
/* Remove waiter from waiting list */
mr_list_del(&wait->list);
/* Set waiter event */
wait->event = event;
/* Unlock */
mr_spinlock_unlock_irqrestore(&__lock, mask);
return 0;
}
static void wait_release(mr_object_t *obj) {
mr_waiter_t *waiter;
/* Get waiter */
waiter = MR_CONTAINER_OF(obj, mr_waiter_t, parent);
/* Remove waiter */
waiter_remove(waiter);
}
static void wait_release_dyn(mr_object_t *obj) {
/* Release waiter */
wait_release(obj);
/* Free waiter */
mr_free(obj);
}
#endif /* defined(MR_USE_WAIT) */