/** * @copyright (c) 2024-2025, MacRsh * * @license SPDX-License-Identifier: Apache-2.0 * * @date 2024-09-06 MacRsh First version */ #include #if defined(MR_USE_WAIT) #include #include /* 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) */