[kernel] fix the timer issue

This commit is contained in:
Bernard Xiong 2020-09-30 23:50:18 +08:00
parent c758168877
commit 1c6700acfd
1 changed files with 58 additions and 27 deletions

View File

@ -25,6 +25,10 @@
static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
#ifdef RT_USING_TIMER_SOFT #ifdef RT_USING_TIMER_SOFT
#define RT_SOFT_TIMER_IDLE 1
#define RT_SOFT_TIMER_BUSY 0
#ifndef RT_TIMER_THREAD_STACK_SIZE #ifndef RT_TIMER_THREAD_STACK_SIZE
#define RT_TIMER_THREAD_STACK_SIZE 512 #define RT_TIMER_THREAD_STACK_SIZE 512
#endif #endif
@ -33,6 +37,8 @@ static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
#define RT_TIMER_THREAD_PRIO 0 #define RT_TIMER_THREAD_PRIO 0
#endif #endif
/* soft timer status */
static rt_uint8_t soft_timer_status = RT_SOFT_TIMER_IDLE;
/* soft timer list */ /* soft timer list */
static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
static struct rt_thread timer_thread; static struct rt_thread timer_thread;
@ -221,6 +227,8 @@ rt_err_t rt_timer_detach(rt_timer_t timer)
level = rt_hw_interrupt_disable(); level = rt_hw_interrupt_disable();
_rt_timer_remove(timer); _rt_timer_remove(timer);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* enable interrupt */ /* enable interrupt */
rt_hw_interrupt_enable(level); rt_hw_interrupt_enable(level);
@ -284,6 +292,8 @@ rt_err_t rt_timer_delete(rt_timer_t timer)
level = rt_hw_interrupt_disable(); level = rt_hw_interrupt_disable();
_rt_timer_remove(timer); _rt_timer_remove(timer);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* enable interrupt */ /* enable interrupt */
rt_hw_interrupt_enable(level); rt_hw_interrupt_enable(level);
@ -408,7 +418,8 @@ rt_err_t rt_timer_start(rt_timer_t timer)
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{ {
/* check whether timer thread is ready */ /* check whether timer thread is ready */
if ((timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) if ((soft_timer_status == RT_SOFT_TIMER_IDLE) &&
((timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND))
{ {
/* resume timer thread to check soft timer */ /* resume timer thread to check soft timer */
rt_thread_resume(&timer_thread); rt_thread_resume(&timer_thread);
@ -445,13 +456,12 @@ rt_err_t rt_timer_stop(rt_timer_t timer)
level = rt_hw_interrupt_disable(); level = rt_hw_interrupt_disable();
_rt_timer_remove(timer); _rt_timer_remove(timer);
/* change status */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* enable interrupt */ /* enable interrupt */
rt_hw_interrupt_enable(level); rt_hw_interrupt_enable(level);
/* change stat */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
return RT_EOK; return RT_EOK;
} }
RTM_EXPORT(rt_timer_stop); RTM_EXPORT(rt_timer_stop);
@ -467,10 +477,13 @@ RTM_EXPORT(rt_timer_stop);
*/ */
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
{ {
register rt_base_t level;
/* timer check */ /* timer check */
RT_ASSERT(timer != RT_NULL); RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
level = rt_hw_interrupt_disable();
switch (cmd) switch (cmd)
{ {
case RT_TIMER_CTRL_GET_TIME: case RT_TIMER_CTRL_GET_TIME:
@ -505,6 +518,7 @@ rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
default: default:
break; break;
} }
rt_hw_interrupt_enable(level);
return RT_EOK; return RT_EOK;
} }
@ -521,6 +535,7 @@ void rt_timer_check(void)
struct rt_timer *t; struct rt_timer *t;
rt_tick_t current_tick; rt_tick_t current_tick;
register rt_base_t level; register rt_base_t level;
rt_list_t list = RT_LIST_OBJECT_INIT(list);
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n")); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n"));
@ -544,7 +559,12 @@ void rt_timer_check(void)
/* remove timer from timer list firstly */ /* remove timer from timer list firstly */
_rt_timer_remove(t); _rt_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
/* add timer to temporary list */
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
/* call timeout function */ /* call timeout function */
t->timeout_func(t->parameter); t->timeout_func(t->parameter);
@ -554,6 +574,12 @@ void rt_timer_check(void)
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick)); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
/* Check whether the timer object is detached or started again */
if (rt_list_isempty(&list))
{
continue;
}
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED)) (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{ {
@ -561,14 +587,8 @@ void rt_timer_check(void)
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t); rt_timer_start(t);
} }
else
{
/* stop timer */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
} }
else else break;
break;
} }
/* enable interrupt */ /* enable interrupt */
@ -589,18 +609,20 @@ rt_tick_t rt_timer_next_timeout_tick(void)
#ifdef RT_USING_TIMER_SOFT #ifdef RT_USING_TIMER_SOFT
/** /**
* This function will check timer list, if a timeout event happens, the * This function will check software-timer list, if a timeout event happens, the
* corresponding timeout function will be invoked. * corresponding timeout function will be invoked.
*/ */
void rt_soft_timer_check(void) void rt_soft_timer_check(void)
{ {
rt_tick_t current_tick; rt_tick_t current_tick;
struct rt_timer *t; struct rt_timer *t;
register rt_base_t level;
rt_list_t list = RT_LIST_OBJECT_INIT(list);
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n")); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n"));
/* lock scheduler */ /* disable interrupt */
rt_enter_critical(); level = rt_hw_interrupt_disable();
while (!rt_list_isempty(&rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) while (!rt_list_isempty(&rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{ {
@ -619,17 +641,32 @@ void rt_soft_timer_check(void)
/* remove timer from timer list firstly */ /* remove timer from timer list firstly */
_rt_timer_remove(t); _rt_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
/* add timer to temporary list */
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
soft_timer_status = RT_SOFT_TIMER_BUSY;
/* enable interrupt */
rt_hw_interrupt_enable(level);
/* not lock scheduler when performing timeout function */
rt_exit_critical();
/* call timeout function */ /* call timeout function */
t->timeout_func(t->parameter); t->timeout_func(t->parameter);
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick)); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
/* lock scheduler */ /* disable interrupt */
rt_enter_critical(); level = rt_hw_interrupt_disable();
soft_timer_status = RT_SOFT_TIMER_IDLE;
/* Check whether the timer object is detached or started again */
if (rt_list_isempty(&list))
{
continue;
}
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED)) (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
@ -638,17 +675,11 @@ void rt_soft_timer_check(void)
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t); rt_timer_start(t);
} }
else
{
/* stop timer */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
} }
else break; /* not check anymore */ else break; /* not check anymore */
} }
/* enable interrupt */
/* unlock scheduler */ rt_hw_interrupt_enable(level);
rt_exit_critical();
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n")); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
} }