xiuos3/kernel/thread/softtimer.c

357 lines
8.6 KiB
C
Raw Normal View History

2021-04-28 17:49:18 +08:00
/*
* Copyright (c) 2020 AIIT XUOS Lab
* XiUOS is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
/**
* @file: softtimer.c
* @brief: softtimer file
* @version: 1.0
* @author: AIIT XUOS Lab
* @date: 2020/3/15
*
*/
#include <xiuos.h>
DoubleLinklistType xiaoshan_timer_sort_head = {&xiaoshan_timer_sort_head, &xiaoshan_timer_sort_head};
DoubleLinklistType k_timer_list = {&k_timer_list, &k_timer_list};
DECLARE_ID_MANAGER(k_softtimer_id_manager, ID_NUM_MAX);
extern queue *sys_workq;
void _Init(struct Timer *timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
x_ticks_t time,
uint8 trigger_mode)
{
x_base lock = 0;
NULL_PARAM_CHECK(timer);
strncpy(timer->name, name, NAME_NUM_MAX);
// insert to link
lock = CriticalAreaLock();
DoubleLinkListInsertNodeAfter(&(k_timer_list), &(timer->link));
CriticalAreaUnLock(lock);
timer->trigger_mode = trigger_mode;
timer->active_status = TIMER_ACTIVE_FALSE;
timer->func_callback = timeout;
timer->param = parameter;
timer->deadline_timeslice = 0;
timer->origin_timeslice = time;
timer->prio = GetKTaskDescriptor()->task_base_info.origin_prio;
}
x_err_t _Delete(TimerType timer)
{
x_base lock = 0;
NULL_PARAM_CHECK(timer);
lock = CriticalAreaLock();
DoubleLinkListRmNode(&(timer->sortlist));
DoubleLinkListRmNode(&(timer->link));
CriticalAreaUnLock(lock);
KERNEL_FREE(timer);
timer = NONE;
return EOK;
}
x_err_t _StartRun(TimerType timer)
{
NULL_PARAM_CHECK(timer);
x_base lock = CriticalAreaLock();
timer->active_status = TIMER_ACTIVE_FALSE;
CHECK(timer->origin_timeslice < TICK_SIZE_MAX / 2);
timer->deadline_timeslice = CurrentTicksGain() + timer->origin_timeslice;
timer->active_status = TIMER_ACTIVE_TRUE;
DoubleLinklistType *pLink = NONE;
TimerType pNode = NONE;
DOUBLE_LINKLIST_FOR_EACH(pLink, &xiaoshan_timer_sort_head) {
pNode = CONTAINER_OF(pLink, struct Timer, sortlist);
if (timer->deadline_timeslice < pNode->deadline_timeslice) {
DoubleLinkListInsertNodeBefore(pLink, &timer->sortlist);
break;
}
}
if (pLink == &xiaoshan_timer_sort_head) {
DoubleLinkListInsertNodeBefore(&xiaoshan_timer_sort_head, &timer->sortlist);
}
CriticalAreaUnLock(lock);
return EOK;
}
// Just stop working, don't free memory
x_err_t _QuitRun(TimerType timer)
{
x_base lock = 0;
NULL_PARAM_CHECK(timer);
if (!(timer->active_status == TIMER_ACTIVE_TRUE))
return -ERROR;
lock = CriticalAreaLock();
timer->active_status = TIMER_ACTIVE_FALSE;
timer->deadline_timeslice = 0;
DoubleLinkListRmNode(&(timer->sortlist));
CriticalAreaUnLock(lock);
return EOK;
}
x_err_t _Modify(TimerType timer, x_ticks_t ticks)
{
NULL_PARAM_CHECK(timer);
if (0 == ticks) {
KPrintf("timeout ticks must be setted more then 0.\n");
return -EINVALED;
}
timer->origin_timeslice = ticks;
return EOK;
}
static struct TimerDone Done =
{
.Init = _Init,
.Delete = _Delete,
.StartRun = _StartRun,
.QuitRun = _QuitRun,
.Modify = _Modify,
};
/**
* This function will create a softtimer.
*
* @param name the length of the msg queue.
* @param timeout the callback of the timer.
* @param parameter the parameter of the callback function
* @param time the timeout time
* @param trigger_mode the trigger way of the timer
*
* @return id on success
*/
int32 KCreateTimer(const char *name,
void (*timeout)(void *parameter),
void *parameter,
x_ticks_t time,
uint8 trigger_mode)
{
struct Timer *timer = NONE;
timer = (struct Timer *)x_malloc(sizeof(struct Timer));
if (timer == NONE) {
return NONE;
}
memset(timer, 0x0, sizeof(struct Timer));
// Generate ID
int32 id = IdInsertObj(&k_softtimer_id_manager, &timer->id_node);
if (id < 0) {
x_free(timer);
return NONE;
}
timer->done = &Done;
timer->done->Init(timer, name, timeout, parameter, time, trigger_mode);
return id;
}
/**
* This function will delete a timer.
*
* @param timer_id the id number of timer.
*
* @return
*/
x_err_t KDeleteTimer(int32 timer_id)
{
TimerType timer = KGetTimer(timer_id);
timer->done->Delete(timer);
}
/**
* This function will startup a timer.
*
* @param timer_id the id number of timer.
*
* @return
*/
x_err_t KTimerStartRun(int32 timer_id)
{
TimerType timer = KGetTimer(timer_id);
return timer->done->StartRun(timer);
}
/**
* This function will stop a timer.
*
* @param timer_id the id number of timer.
*
* @return
*/
x_err_t KTimerQuitRun(int32 timer_id)
{
TimerType timer = KGetTimer(timer_id);
timer->done->QuitRun(timer);
}
/**
* This function will modify the timeout of a timer.
*
* @param timer_id the id number of timer.
* @param ticks timeout ticks
*
* @return
*/
x_err_t KTimerModify(int32 timer_id, x_ticks_t ticks)
{
TimerType timer = KGetTimer(timer_id);
timer->done->Modify(timer, ticks);
}
static void TimerCBEnter(void *param)
{
struct Timer *t = (struct Timer *)param;
t->func_callback(t->param);
free(t->t_work);
}
void timer_work_func(struct Work *work, void *work_data)
{
x_err_t flag;
struct Timer *t = work_data;
int32 timer_work = KTaskCreate("timer_work_thr", TimerCBEnter, t, 2048, t->prio);
flag = StartupKTask(timer_work);
if (flag != EOK) {
KPrintf("timer create callback thread failed .\n");
}
return;
}
void CheckTimerList(void)
{
x_base lock = 0;
struct Timer *t = NONE;
x_ticks_t current_tick = 0;
SYS_KDEBUG_LOG(KDBG_SOFTTIMER, ("timer check enter\n"));
current_tick = CurrentTicksGain();
lock = CriticalAreaLock();
while (!IsDoubleLinkListEmpty(&xiaoshan_timer_sort_head)) {
t = SYS_DOUBLE_LINKLIST_ENTRY(xiaoshan_timer_sort_head.node_next,
struct Timer, sortlist);
if ((current_tick - t->deadline_timeslice) < TICK_SIZE_MAX / 2) {
if (t->active_status == TIMER_ACTIVE_TRUE) {
DoubleLinkListRmNode(&t->sortlist); // Take it off the list first
current_tick = CurrentTicksGain();
SYS_KDEBUG_LOG(KDBG_SOFTTIMER, ("current tick: %d\n", current_tick));
if (t->trigger_mode == TIMER_TRIGGER_PERIODIC) {
t->done->StartRun(t); // Periodic timer, re insert the appropriate position of the list
} else {
t->done->QuitRun(t);
}
// Throw it to the task queue and start a new thread
t->t_work = x_malloc(sizeof(struct Work));
((WorkQueueDoneType *)sys_workq->done)->WorkInit(t->t_work, timer_work_func, t);
CriticalAreaUnLock(lock);
((WorkQueueDoneType *)sys_workq->done)->WorkSubmit((WorkqueueType *)sys_workq->property, t->t_work, 0);
lock = CriticalAreaLock();
} else {
KPrintf("sortlist run unactive timer(%s), quit this timer\n", t->name);
}
}
else
break;
}
CriticalAreaUnLock(lock);
SYS_KDEBUG_LOG(KDBG_SOFTTIMER, ("timer check leave\n"));
}
x_err_t KTimerAssignMemberRun(int32 timer_id, x_ticks_t ticks)
{
TimerType timer = KGetTimer(timer_id);
NULL_PARAM_CHECK(timer);
if (ticks == 0) {
KPrintf("Timeout ticks must be setted more than 0.\n");
return -EINVALED;
}
timer->origin_timeslice = ticks;
return timer->done->StartRun(timer);
}
/**
* This function will get the timer with id.
*
* @param id the id number of timer.
*
* @return timer structrue
*/
TimerType KGetTimer(int32 id)
{
x_base lock = 0;
TimerType timer = NONE;
lock = CriticalAreaLock();
struct IdNode *idnode = IdGetObj(&k_softtimer_id_manager, id);
if (idnode == NONE){
CriticalAreaUnLock(lock);
return NONE;
}
timer = CONTAINER_OF(idnode, struct Timer, id_node);
CriticalAreaUnLock(lock);
return timer;
}
int32 KGetTimerID(TimerType timer)
{
return timer->id_node.id;
}