Files
mkrtos-real/mkrtos_knl/knl/sema.c

302 lines
7.3 KiB
C
Raw Normal View History

2024-08-07 22:51:57 +08:00
2025-02-13 20:45:46 +08:00
#include "sema.h"
2024-08-07 22:51:57 +08:00
#include "atomics.h"
#include "factory.h"
#include "init.h"
2025-02-13 20:45:46 +08:00
#include "kobject.h"
#include "ref.h"
#include "sleep.h"
2025-02-13 20:45:46 +08:00
#include "slist.h"
#include "spinlock.h"
#include "thread.h"
#include "types.h"
2024-08-07 22:51:57 +08:00
#if IS_ENABLED(CONFIG_BUDDY_SLAB)
#include <slab.h>
static slab_t *sema_slab;
#endif
2025-02-16 23:11:18 +08:00
enum SEMA_OP
{
2024-08-07 22:51:57 +08:00
SEMA_UP,
SEMA_DOWN,
};
static void sema_mem_init(void)
{
#if IS_ENABLED(CONFIG_BUDDY_SLAB)
sema_slab = slab_create(sizeof(sema_t), "sema");
assert(sema_slab);
#endif
}
INIT_KOBJ_MEM(sema_mem_init);
2025-02-16 23:11:18 +08:00
typedef struct sema_wait_item
{
2024-08-07 22:51:57 +08:00
slist_head_t node;
thread_t *thread;
} sema_wait_item_t;
static void sema_wait_item_init(sema_wait_item_t *obj, thread_t *th)
{
slist_init(&obj->node);
obj->thread = th;
}
void sema_up(sema_t *obj)
{
assert(obj);
umword_t status;
2025-03-11 17:03:30 +08:00
thread_t *th = thread_get_current();
2024-08-07 22:51:57 +08:00
ref_counter_inc(&obj->ref);
2024-08-07 22:51:57 +08:00
status = spinlock_lock(&obj->lock);
2025-02-16 23:11:18 +08:00
if (slist_is_empty(&obj->suspend_head))
{
if (obj->cnt < obj->max_cnt)
{
2024-08-07 22:51:57 +08:00
obj->cnt++;
}
// printk("up0 sema cnt:%d max:%d.\n", obj->cnt, obj->max_cnt);
2025-02-16 23:11:18 +08:00
}
else
{
2024-08-07 22:51:57 +08:00
slist_head_t *first_wait_node;
sema_wait_item_t *first_wait;
first_wait_node = slist_first(&obj->suspend_head);
first_wait = container_of(first_wait_node, sema_wait_item_t, node);
2025-02-16 23:11:18 +08:00
if (thread_get_status(first_wait->thread) == THREAD_SUSPEND)
{
slist_del(first_wait_node);
if (ref_counter_dec_and_release(&first_wait->thread->ref, &first_wait->thread->kobj) != 1)
{
thread_sleep_del_and_wakeup(first_wait->thread);
}
if (obj->cnt < obj->max_cnt)
{
obj->cnt++;
}
if (obj->max_cnt == 1 && obj->hold_th == &th->kobj)
{
//还原优先级
thread_set_prio(th, obj->hold_th_prio);
obj->hold_th = NULL;
}
2025-02-16 23:11:18 +08:00
}
else
{
// 超时退出,但是切出来的时候没有切到休眠线程,切到了这里。
if (obj->cnt < obj->max_cnt)
{
obj->cnt++;
}
2025-03-11 17:03:30 +08:00
}
2024-08-07 22:51:57 +08:00
}
spinlock_set(&obj->lock, status);
2025-02-16 23:11:18 +08:00
if (cpulock_get_status())
{
preemption();
}
ref_counter_dec_and_release(&obj->ref, &obj->kobj);
2024-08-07 22:51:57 +08:00
}
umword_t sema_down(sema_t *obj, umword_t ticks)
2024-08-07 22:51:57 +08:00
{
assert(obj);
thread_t *th = thread_get_current();
umword_t status;
umword_t remain_sleep = 0;
2024-08-07 22:51:57 +08:00
sema_wait_item_t wait_item;
ref_counter_inc(&obj->ref);
2024-08-07 22:51:57 +08:00
again:
status = spinlock_lock(&obj->lock);
2025-02-16 23:11:18 +08:00
if (obj->cnt == 0)
{
2024-08-07 22:51:57 +08:00
sema_wait_item_init(&wait_item, th);
ref_counter_inc(&th->ref);
2024-08-07 22:51:57 +08:00
slist_add_append(&obj->suspend_head, &wait_item.node);
2025-03-11 17:03:30 +08:00
if (obj->max_cnt == 1 && obj->hold_th)
{
if (thread_get_prio(th) > thread_get_prio((thread_t*)(obj->hold_th)))
{
//执行优先级继承
thread_set_prio(((thread_t*)(obj->hold_th)), thread_get_prio(th));
}
}
remain_sleep = thread_sleep(ticks);
2025-02-16 23:11:18 +08:00
if (remain_sleep == 0 && ticks != 0)
{
2025-02-13 20:45:46 +08:00
// 超时退出的,直接从列表中删除
assert(slist_in_list(&wait_item.node));
slist_del(&wait_item.node);
ref_counter_dec(&th->ref);
}
2025-02-16 23:11:18 +08:00
else
{
2025-02-13 20:45:46 +08:00
spinlock_set(&obj->lock, status);
2025-02-16 23:11:18 +08:00
if (cpulock_get_status())
{
2025-02-13 20:45:46 +08:00
preemption();
}
goto again;
}
2025-02-16 23:11:18 +08:00
}
else
{
2024-08-07 22:51:57 +08:00
assert(obj->cnt > 0);
obj->cnt--;
2025-03-11 17:03:30 +08:00
if (obj->max_cnt == 1)
{
obj->hold_th = &th->kobj;
obj->hold_th_prio = thread_get_prio(th);
}
2024-08-07 22:51:57 +08:00
}
spinlock_set(&obj->lock, status);
ref_counter_dec_and_release(&obj->ref, &obj->kobj);
return remain_sleep;
2024-08-07 22:51:57 +08:00
}
static void sema_syscall(kobject_t *kobj, syscall_prot_t sys_p,
msg_tag_t in_tag, entry_frame_t *f)
{
sema_t *sema = container_of(kobj, sema_t, kobj);
msg_tag_t tag = msg_tag_init4(0, 0, 0, -EINVAL);
task_t *task = thread_get_current_task();
2025-02-16 23:11:18 +08:00
if (sys_p.prot != SEMA_PROT)
{
2024-08-07 22:51:57 +08:00
f->regs[0] = msg_tag_init4(0, 0, 0, -EPROTO).raw;
return;
}
2025-02-16 23:11:18 +08:00
switch (sys_p.op)
{
case SEMA_UP:
{
2024-08-07 22:51:57 +08:00
sema_up(sema);
tag = msg_tag_init4(0, 0, 0, 0);
2025-02-16 23:11:18 +08:00
}
break;
case SEMA_DOWN:
{
umword_t ret;
ret = sema_down(sema, f->regs[0]);
f->regs[1] = ret;
2024-08-07 22:51:57 +08:00
tag = msg_tag_init4(0, 0, 0, 0);
}
}
f->regs[0] = tag.raw;
}
static sema_t *sema_create(ram_limit_t *lim, umword_t cnt, umword_t max)
{
sema_t *kobj = NULL;
#if IS_ENABLED(CONFIG_BUDDY_SLAB)
kobj = mm_limit_alloc_slab(sema_slab, lim);
#else
kobj = mm_limit_alloc(lim, sizeof(sema_t));
2024-08-07 22:51:57 +08:00
#endif
2025-02-16 23:11:18 +08:00
if (!kobj)
{
2024-08-07 22:51:57 +08:00
return NULL;
}
sema_init(kobj, cnt, max);
return kobj;
}
static void sema_unmap(obj_space_t *obj_space, kobject_t *kobj)
{
}
static void sema_release_stage1(kobject_t *kobj)
{
sema_t *obj = container_of(kobj, sema_t, kobj);
2024-08-07 22:51:57 +08:00
kobject_invalidate(kobj);
#if 1
umword_t status;
status = spinlock_lock(&obj->lock);
sema_wait_item_t *wait_item;
slist_foreach_not_next(wait_item, &obj->suspend_head, node)
{
sema_wait_item_t *next = slist_next_entry(wait_item, &obj->suspend_head, node);
slist_del(&wait_item->node);
if (ref_counter_dec_and_release(&wait_item->thread->ref, &wait_item->thread->kobj) != 1)
{
thread_sleep_del_and_wakeup(wait_item->thread);
}
2025-02-16 23:11:18 +08:00
if (obj->cnt < obj->max_cnt)
{
obj->cnt++;
}
wait_item = next;
}
spinlock_set(&obj->lock, status);
#endif
}
static bool_t sema_put(kobject_t *kobj)
{
sema_t *th = container_of(kobj, sema_t, kobj);
return ref_counter_dec(&th->ref) == 1;
2024-08-07 22:51:57 +08:00
}
static void sema_release_stage2(kobject_t *kobj)
{
sema_t *obj = container_of(kobj, sema_t, kobj);
#if IS_ENABLED(CONFIG_BUDDY_SLAB)
mm_limit_free_slab(sema_slab, thread_get_current_task()->lim, obj);
#else
mm_limit_free(thread_get_current_task()->lim, obj);
#endif
printk("sema 0x%x free.\n", kobj);
2024-08-07 22:51:57 +08:00
}
void sema_init(sema_t *obj, int cnt, int max)
{
2025-02-16 23:11:18 +08:00
if (max <= 0)
{
2024-08-07 22:51:57 +08:00
max = 1;
}
2025-02-16 23:11:18 +08:00
if (cnt < 0)
{
2024-08-07 22:51:57 +08:00
cnt = 0;
}
2025-02-16 23:11:18 +08:00
if (cnt > max)
{
2024-08-07 22:51:57 +08:00
cnt = max;
}
obj->cnt = cnt;
kobject_init(&obj->kobj, SEMA_TYPE);
spinlock_init(&obj->lock);
slist_init(&obj->suspend_head);
ref_counter_init(&obj->ref);
ref_counter_inc(&obj->ref);
2024-08-07 22:51:57 +08:00
obj->max_cnt = max <= 0 ? 1 : max;
obj->kobj.invoke_func = sema_syscall;
obj->kobj.stage_1_func = sema_release_stage1;
obj->kobj.stage_2_func = sema_release_stage2;
obj->kobj.put_func = sema_put;
2024-08-07 22:51:57 +08:00
// printk("sema init cnt:%d max:%d.\n", cnt, max);
2024-08-07 22:51:57 +08:00
}
static kobject_t *sema_func(ram_limit_t *lim, umword_t arg0, umword_t arg1,
umword_t arg2, umword_t arg3)
{
sema_t *sema = sema_create(lim, arg0, arg1);
2025-02-16 23:11:18 +08:00
if (!sema)
{
2024-08-07 22:51:57 +08:00
return NULL;
}
return &sema->kobj;
}
/**
* @brief
*
*/
static void sema_factory_register(void)
{
factory_register(sema_func, SEMA_PROT);
}
INIT_KOBJ(sema_factory_register);