Add schedule node midway
This commit is contained in:
@@ -33,65 +33,123 @@ Modification:
|
||||
struct Thread* max_priority_runnable_task(void)
|
||||
{
|
||||
static struct Thread* task = NULL;
|
||||
static int priority = 0;
|
||||
// static int priority = 0;
|
||||
|
||||
priority = __builtin_ffs(ready_task_priority) - 1;
|
||||
if (priority > 31 || priority < 0) {
|
||||
return NULL;
|
||||
}
|
||||
// priority = __builtin_ffs(ready_task_priority) - 1;
|
||||
// if (priority > 31 || priority < 0) {
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
DOUBLE_LIST_FOR_EACH_ENTRY(task, &xizi_task_manager.task_list_head[priority], node)
|
||||
{
|
||||
assert(task != NULL);
|
||||
if (task->state == READY && !task->dead) {
|
||||
// found a runnable task, stop this look up
|
||||
return task;
|
||||
} else if (task->dead && task->state != RUNNING) {
|
||||
// DOUBLE_LIST_FOR_EACH_ENTRY(task, &xizi_task_manager.task_list_head[priority], node)
|
||||
// {
|
||||
// assert(task != NULL);
|
||||
// if (task->state == READY && !task->dead) {
|
||||
// // found a runnable task, stop this look up
|
||||
// return task;
|
||||
// } else if (task->dead && task->state != RUNNING) {
|
||||
|
||||
struct TaskLifecycleOperations* tlo = GetSysObject(struct TaskLifecycleOperations, &xizi_task_manager.task_lifecycle_ops_tag);
|
||||
tlo->free_pcb(task);
|
||||
return NULL;
|
||||
}
|
||||
// struct TaskLifecycleOperations* tlo = GetSysObject(struct TaskLifecycleOperations, &xizi_task_manager.task_lifecycle_ops_tag);
|
||||
// tlo->free_pcb(task);
|
||||
// return NULL;
|
||||
// }
|
||||
// }
|
||||
if (!rbt_is_empty(&g_scheduler.snode_state_pool[READY])) {
|
||||
return ((struct ScheduleNode*)(g_scheduler.snode_state_pool[READY].root->data))->pthd;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct Thread* round_robin_runnable_task(uint32_t priority)
|
||||
#include "multicores.h"
|
||||
#include "rbtree.h"
|
||||
#include "task.h"
|
||||
|
||||
bool init_schedule_node(struct ScheduleNode* snode, struct Thread* bind_thd)
|
||||
{
|
||||
struct Thread* task = NULL;
|
||||
|
||||
DOUBLE_LIST_FOR_EACH_ENTRY(task, &xizi_task_manager.task_list_head[priority], node)
|
||||
{
|
||||
if (task->state == READY && !task->dead) {
|
||||
// found a runnable task, stop this look up
|
||||
return task;
|
||||
} else if (task->dead && task->state != RUNNING) {
|
||||
struct TaskLifecycleOperations* tlo = GetSysObject(struct TaskLifecycleOperations, &xizi_task_manager.task_lifecycle_ops_tag);
|
||||
tlo->free_pcb(task);
|
||||
return NULL;
|
||||
}
|
||||
snode->pthd = bind_thd;
|
||||
snode->snode_id = bind_thd->tid;
|
||||
snode->sched_context.remain_tick = 0;
|
||||
snode->sleep_context.remain_ms = 0;
|
||||
snode->state = INIT;
|
||||
if (RBTTREE_INSERT_SECC != rbt_insert(&g_scheduler.snode_state_pool[INIT], //
|
||||
snode->snode_id, (void*)snode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* recover task priority */
|
||||
void recover_priority(void)
|
||||
bool task_trans_sched_state(struct ScheduleNode* snode, RbtTree* from_pool, RbtTree* to_pool, enum ThreadState target_state)
|
||||
{
|
||||
struct Thread* task = NULL;
|
||||
for (int i = 1; i < TASK_MAX_PRIORITY; i++) {
|
||||
if (i == TASK_DEFAULT_PRIORITY)
|
||||
continue;
|
||||
DOUBLE_LIST_FOR_EACH_ENTRY(task, &xizi_task_manager.task_list_head[i], node)
|
||||
{
|
||||
if (!IS_DOUBLE_LIST_EMPTY(&task->node)) {
|
||||
// DEBUG("%s priority recover\n", task->name);
|
||||
task->priority = TASK_DEFAULT_PRIORITY;
|
||||
doubleListDel(&task->node);
|
||||
doubleListAddOnBack(&task->node, &xizi_task_manager.task_list_head[task->priority]);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(snode->snode_id != UNINIT_SNODE_ID && snode->pthd != NULL);
|
||||
if (RBTTREE_DELETE_SUCC != rbt_delete(from_pool, snode->snode_id)) {
|
||||
DEBUG("Thread %d not in from schedule pool\n", snode->pthd->tid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (RBTTREE_INSERT_SECC != rbt_insert(to_pool, snode->snode_id, (void*)snode)) {
|
||||
DEBUG("Thread %d trans state failed\n", snode->pthd->tid);
|
||||
return false;
|
||||
}
|
||||
|
||||
snode->state = target_state;
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_dead(struct Thread* thd)
|
||||
{
|
||||
assert(thd != NULL);
|
||||
struct ScheduleNode* snode = &thd->snode;
|
||||
enum ThreadState thd_cur_state = snode->state;
|
||||
|
||||
assert(snode->state == READY);
|
||||
|
||||
bool trans_res = task_trans_sched_state(snode, //
|
||||
&g_scheduler.snode_state_pool[READY], //
|
||||
&g_scheduler.snode_state_pool[DEAD], DEAD);
|
||||
assert(trans_res = true);
|
||||
return;
|
||||
}
|
||||
|
||||
void task_block(struct Thread* thd)
|
||||
{
|
||||
assert(thd != NULL);
|
||||
struct ScheduleNode* snode = &thd->snode;
|
||||
enum ThreadState thd_cur_state = snode->state;
|
||||
|
||||
assert(thd_cur_state != RUNNING);
|
||||
|
||||
bool trans_res = task_trans_sched_state(snode, //
|
||||
&g_scheduler.snode_state_pool[thd_cur_state], //
|
||||
&g_scheduler.snode_state_pool[BLOCKED], BLOCKED);
|
||||
assert(trans_res = true);
|
||||
return;
|
||||
}
|
||||
|
||||
void task_into_ready(struct Thread* thd)
|
||||
{
|
||||
assert(thd != NULL);
|
||||
struct ScheduleNode* snode = &thd->snode;
|
||||
enum ThreadState thd_cur_state = snode->state;
|
||||
|
||||
bool trans_res = task_trans_sched_state(snode, //
|
||||
&g_scheduler.snode_state_pool[thd_cur_state], //
|
||||
&g_scheduler.snode_state_pool[READY], READY);
|
||||
snode->sched_context.remain_tick = TASK_CLOCK_TICK;
|
||||
assert(trans_res = true);
|
||||
return;
|
||||
}
|
||||
|
||||
void task_yield(struct Thread* thd)
|
||||
{
|
||||
assert(thd != NULL);
|
||||
struct ScheduleNode* snode = &thd->snode;
|
||||
enum ThreadState thd_cur_state = snode->state;
|
||||
|
||||
assert(thd == cur_cpu()->task && thd_cur_state == RUNNING);
|
||||
cur_cpu()->task = NULL;
|
||||
|
||||
bool trans_res = task_trans_sched_state(snode, //
|
||||
&g_scheduler.snode_state_pool[thd_cur_state], //
|
||||
&g_scheduler.snode_state_pool[READY], READY);
|
||||
assert(trans_res = true);
|
||||
return;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ sem_id_t ksemaphore_alloc(struct XiziSemaphorePool* sem_pool, sem_val_t val)
|
||||
}
|
||||
sem->val = val;
|
||||
doubleListNodeInit(&sem->sem_list_node);
|
||||
doubleListNodeInit(&sem->wait_list_guard);
|
||||
rbtree_init(&sem->wait_thd_tree);
|
||||
|
||||
if (0 != rbt_insert(&sem_pool->sem_pool_map, sem->id, sem)) {
|
||||
slab_free(&sem_pool->allocator, sem);
|
||||
@@ -88,7 +88,7 @@ bool ksemaphore_consume(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id, sem
|
||||
bool ksemaphore_wait(struct XiziSemaphorePool* sem_pool, struct Thread* thd, sem_id_t sem_id)
|
||||
{
|
||||
assert(thd != NULL);
|
||||
assert(thd->state == RUNNING);
|
||||
assert(thd->snode.state == RUNNING);
|
||||
/* find sem */
|
||||
struct ksemaphore* sem = ksemaphore_get_by_id(sem_pool, sem_id);
|
||||
// invalid sem id
|
||||
@@ -105,8 +105,9 @@ bool ksemaphore_wait(struct XiziSemaphorePool* sem_pool, struct Thread* thd, sem
|
||||
|
||||
// waiting at the sem
|
||||
sem->val--;
|
||||
xizi_task_manager.task_yield_noschedule(thd, false);
|
||||
xizi_task_manager.task_block(&sem->wait_list_guard, thd);
|
||||
task_yield(thd);
|
||||
task_block(thd);
|
||||
assert(RBTTREE_INSERT_SECC == rbt_insert(&sem->wait_thd_tree, thd->tid, thd));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -120,12 +121,11 @@ bool ksemaphore_signal(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id)
|
||||
}
|
||||
|
||||
if (sem->val < 0) {
|
||||
if (!IS_DOUBLE_LIST_EMPTY(&sem->wait_list_guard)) {
|
||||
struct Thread* thd = CONTAINER_OF(sem->wait_list_guard.next, struct Thread, node);
|
||||
assert(thd != NULL && thd->state == BLOCKED);
|
||||
xizi_task_manager.task_unblock(thd);
|
||||
// DEBUG("waking %s\n", thd->name);
|
||||
}
|
||||
assert(!rbt_is_empty(&sem->wait_thd_tree));
|
||||
RbtNode* root = sem->wait_thd_tree.root;
|
||||
struct Thread* thd = (struct Thread*)root->data;
|
||||
rbt_delete(&sem->wait_thd_tree, root->key);
|
||||
task_into_ready(thd);
|
||||
}
|
||||
|
||||
sem->val++;
|
||||
@@ -155,11 +155,7 @@ bool ksemaphore_free(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id)
|
||||
}
|
||||
|
||||
struct Thread* thd = NULL;
|
||||
DOUBLE_LIST_FOR_EACH_ENTRY(thd, &sem->wait_list_guard, node)
|
||||
{
|
||||
assert(thd != NULL);
|
||||
xizi_task_manager.task_unblock(thd);
|
||||
}
|
||||
// by design: no waking any waiting threads
|
||||
|
||||
rbt_delete(&sem_pool->sem_pool_map, sem_id);
|
||||
doubleListDel(&sem->sem_list_node);
|
||||
|
||||
@@ -44,28 +44,9 @@ struct CPU global_cpus[NR_CPU];
|
||||
uint32_t ready_task_priority;
|
||||
|
||||
struct GlobalTaskPool global_task_pool;
|
||||
struct Scheduler g_scheduler;
|
||||
extern struct TaskLifecycleOperations task_lifecycle_ops;
|
||||
|
||||
static inline void task_node_leave_list(struct Thread* task)
|
||||
{
|
||||
doubleListDel(&task->node);
|
||||
if (IS_DOUBLE_LIST_EMPTY(&xizi_task_manager.task_list_head[task->priority])) {
|
||||
ready_task_priority &= ~((uint32_t)1 << task->priority);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void task_node_add_to_ready_list_head(struct Thread* task)
|
||||
{
|
||||
doubleListAddOnHead(&task->node, &xizi_task_manager.task_list_head[task->priority]);
|
||||
ready_task_priority |= ((uint32_t)1 << task->priority);
|
||||
}
|
||||
|
||||
static inline void task_node_add_to_ready_list_back(struct Thread* task)
|
||||
{
|
||||
doubleListAddOnBack(&task->node, &xizi_task_manager.task_list_head[task->priority]);
|
||||
ready_task_priority |= ((uint32_t)1 << task->priority);
|
||||
}
|
||||
|
||||
static void _task_manager_init()
|
||||
{
|
||||
assert(CreateResourceTag(&xizi_task_manager.task_lifecycle_ops_tag, &xizi_task_manager.tag, //
|
||||
@@ -93,6 +74,14 @@ static void _task_manager_init()
|
||||
doubleListNodeInit(&global_task_pool.thd_listing_head);
|
||||
rbtree_init(&global_task_pool.thd_ref_map);
|
||||
|
||||
// scheduler
|
||||
assert(CreateResourceTag(&g_scheduler.tag, &xizi_task_manager.tag, //
|
||||
"GlobalScheduler", TRACER_SYSOBJECT, (void*)&g_scheduler));
|
||||
semaphore_pool_init(&g_scheduler.semaphore_pool);
|
||||
for (int pool_id = 0; pool_id < NR_STATE; pool_id++) {
|
||||
rbtree_init(&g_scheduler.snode_state_pool[pool_id]);
|
||||
}
|
||||
|
||||
// tid pool
|
||||
xizi_task_manager.next_pid = 0;
|
||||
|
||||
@@ -125,8 +114,8 @@ int _task_return_sys_resources(struct Thread* ptask)
|
||||
// @todo fix memory leak
|
||||
} else {
|
||||
assert(!queue_is_empty(&server_to_info->sessions_to_be_handle));
|
||||
if (server_to_info->state == BLOCKED) {
|
||||
xizi_task_manager.task_unblock(session_backend->server);
|
||||
if (server_to_info->snode.state == BLOCKED) {
|
||||
task_into_ready(server_to_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +173,7 @@ static void _free_thread(struct Thread* task)
|
||||
}
|
||||
|
||||
// remove thread from used task list
|
||||
task_node_leave_list(task);
|
||||
task_dead(task);
|
||||
|
||||
/* free memspace if needed to */
|
||||
if (task->memspace != NULL) {
|
||||
@@ -196,8 +185,8 @@ static void _free_thread(struct Thread* task)
|
||||
// awake deamon in this memspace
|
||||
if (task->memspace->thread_to_notify != NULL) {
|
||||
if (task->memspace->thread_to_notify != task) {
|
||||
if (task->memspace->thread_to_notify->state == BLOCKED) {
|
||||
xizi_task_manager.task_unblock(task->memspace->thread_to_notify);
|
||||
if (task->memspace->thread_to_notify->snode.state == BLOCKED) {
|
||||
task_into_ready(task->memspace->thread_to_notify);
|
||||
} else {
|
||||
task->memspace->thread_to_notify->advance_unblock = true;
|
||||
}
|
||||
@@ -231,9 +220,17 @@ static struct Thread* _new_thread(struct MemSpace* pmemspace)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// [schedule related]
|
||||
if (!init_schedule_node(&task->snode, task)) {
|
||||
ERROR("Not enough memory\n");
|
||||
slab_free(&xizi_task_manager.task_allocator, (void*)task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// alloc stack page for task
|
||||
if ((void*)(task->thread_context.kern_stack_addr = (uintptr_t)kalloc_by_ownership(pmemspace->kernspace_mem_usage.tag, USER_STACK_SIZE)) == NULL) {
|
||||
/* here inside, will no free memspace */
|
||||
assert(RBTTREE_DELETE_SUCC == rbt_delete(&g_scheduler.snode_state_pool[INIT], task->snode.snode_id));
|
||||
slab_free(&xizi_task_manager.task_allocator, (void*)task);
|
||||
return NULL;
|
||||
}
|
||||
@@ -279,7 +276,6 @@ static struct Thread* _new_thread(struct MemSpace* pmemspace)
|
||||
}
|
||||
|
||||
// [name]
|
||||
// [schedule related]
|
||||
|
||||
return task;
|
||||
}
|
||||
@@ -289,22 +285,12 @@ struct TaskLifecycleOperations task_lifecycle_ops = {
|
||||
.free_pcb = _free_thread,
|
||||
};
|
||||
|
||||
static void _task_set_default_schedule_attr(struct Thread* task)
|
||||
{
|
||||
task->remain_tick = TASK_CLOCK_TICK;
|
||||
task->maxium_tick = TASK_CLOCK_TICK * 10;
|
||||
task->dead = false;
|
||||
task->state = READY;
|
||||
task->priority = TASK_DEFAULT_PRIORITY;
|
||||
task_node_add_to_ready_list_head(task);
|
||||
}
|
||||
|
||||
static void task_state_set_running(struct Thread* task)
|
||||
{
|
||||
assert(task != NULL && task->state == READY);
|
||||
task->state = RUNNING;
|
||||
task_node_leave_list(task);
|
||||
doubleListAddOnHead(&task->node, &xizi_task_manager.task_running_list_head);
|
||||
assert(task != NULL && task->snode.state == READY);
|
||||
task_trans_sched_state(&task->snode, //
|
||||
&g_scheduler.snode_state_pool[READY], //
|
||||
&g_scheduler.snode_state_pool[RUNNING], RUNNING);
|
||||
}
|
||||
|
||||
struct Thread* next_task_emergency = NULL;
|
||||
@@ -319,7 +305,7 @@ static void _scheduler(struct SchedulerRightGroup right_group)
|
||||
next_task = NULL;
|
||||
/* find next runnable task */
|
||||
assert(cur_cpu()->task == NULL);
|
||||
if (next_task_emergency != NULL && next_task_emergency->state == READY) {
|
||||
if (next_task_emergency != NULL && next_task_emergency->snode.state == READY) {
|
||||
next_task = next_task_emergency;
|
||||
} else {
|
||||
next_task = xizi_task_manager.next_runnable_task();
|
||||
@@ -340,76 +326,21 @@ static void _scheduler(struct SchedulerRightGroup right_group)
|
||||
assert(next_task->memspace->pgdir.pd_addr != NULL);
|
||||
p_mmu_driver->LoadPgdir((uintptr_t)V2P(next_task->memspace->pgdir.pd_addr));
|
||||
context_switch(&cpu->scheduler, next_task->thread_context.context);
|
||||
assert(next_task->state != RUNNING);
|
||||
assert(next_task->snode.state != RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
static void _task_yield_noschedule(struct Thread* task, bool blocking)
|
||||
{
|
||||
assert(task != NULL);
|
||||
/// @warning only support current task yield now
|
||||
assert(task == cur_cpu()->task && task->state == RUNNING);
|
||||
|
||||
// rearrage current task position
|
||||
task_node_leave_list(task);
|
||||
if (task->state == RUNNING) {
|
||||
task->state = READY;
|
||||
}
|
||||
task->remain_tick = TASK_CLOCK_TICK;
|
||||
cur_cpu()->task = NULL;
|
||||
task_node_add_to_ready_list_back(task);
|
||||
}
|
||||
|
||||
static void _task_block(struct double_list_node* head, struct Thread* task)
|
||||
{
|
||||
assert(head != NULL);
|
||||
assert(task != NULL);
|
||||
assert(task->state != RUNNING);
|
||||
task_node_leave_list(task);
|
||||
task->state = BLOCKED;
|
||||
doubleListAddOnHead(&task->node, head);
|
||||
}
|
||||
|
||||
static void _task_unblock(struct Thread* task)
|
||||
{
|
||||
assert(task != NULL);
|
||||
assert(task->state == BLOCKED || task->state == SLEEPING);
|
||||
task_node_leave_list(task);
|
||||
task->state = READY;
|
||||
task_node_add_to_ready_list_back(task);
|
||||
}
|
||||
|
||||
/// @brief @warning not tested function
|
||||
/// @param priority
|
||||
static void _set_cur_task_priority(int priority)
|
||||
{
|
||||
if (priority < 0 || priority >= TASK_MAX_PRIORITY) {
|
||||
ERROR("priority is invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct Thread* current_task = cur_cpu()->task;
|
||||
assert(current_task != NULL && current_task->state == RUNNING);
|
||||
|
||||
task_node_leave_list(current_task);
|
||||
|
||||
current_task->priority = priority;
|
||||
|
||||
task_node_add_to_ready_list_back(current_task);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
struct XiziTaskManager xizi_task_manager = {
|
||||
.init = _task_manager_init,
|
||||
.task_set_default_schedule_attr = _task_set_default_schedule_attr,
|
||||
|
||||
.next_runnable_task = max_priority_runnable_task,
|
||||
.task_scheduler = _scheduler,
|
||||
|
||||
.task_block = _task_block,
|
||||
.task_unblock = _task_unblock,
|
||||
.task_yield_noschedule = _task_yield_noschedule,
|
||||
.set_cur_task_priority = _set_cur_task_priority
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user