Browse Source

[kernel] fix the timer issue

Bernard Xiong 4 years ago
parent
commit
1c6700acfd
1 changed files with 58 additions and 27 deletions
  1. 58 27
      src/timer.c

+ 58 - 27
src/timer.c

@@ -25,6 +25,10 @@
 static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
 
 #ifdef RT_USING_TIMER_SOFT
+
+#define RT_SOFT_TIMER_IDLE              1
+#define RT_SOFT_TIMER_BUSY              0
+
 #ifndef RT_TIMER_THREAD_STACK_SIZE
 #define RT_TIMER_THREAD_STACK_SIZE     512
 #endif
@@ -33,6 +37,8 @@ static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
 #define RT_TIMER_THREAD_PRIO           0
 #endif
 
+/* soft timer status */
+static rt_uint8_t soft_timer_status = RT_SOFT_TIMER_IDLE;
 /* soft timer list */
 static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
 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();
 
     _rt_timer_remove(timer);
+    /* stop timer */
+    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
 
     /* enable interrupt */
     rt_hw_interrupt_enable(level);
@@ -284,6 +292,8 @@ rt_err_t rt_timer_delete(rt_timer_t timer)
     level = rt_hw_interrupt_disable();
 
     _rt_timer_remove(timer);
+    /* stop timer */
+    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
 
     /* enable interrupt */
     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)
     {
         /* 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 */
             rt_thread_resume(&timer_thread);
@@ -445,13 +456,12 @@ rt_err_t rt_timer_stop(rt_timer_t timer)
     level = rt_hw_interrupt_disable();
 
     _rt_timer_remove(timer);
+    /* change status */
+    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
 
     /* enable interrupt */
     rt_hw_interrupt_enable(level);
 
-    /* change stat */
-    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
-
     return RT_EOK;
 }
 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)
 {
+    register rt_base_t level;
+
     /* timer check */
     RT_ASSERT(timer != RT_NULL);
     RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
 
+    level = rt_hw_interrupt_disable();
     switch (cmd)
     {
     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:
         break;
     }
+    rt_hw_interrupt_enable(level);
 
     return RT_EOK;
 }
@@ -521,6 +535,7 @@ void rt_timer_check(void)
     struct rt_timer *t;
     rt_tick_t current_tick;
     register rt_base_t level;
+    rt_list_t list = RT_LIST_OBJECT_INIT(list);
 
     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 */
             _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 */
             t->timeout_func(t->parameter);
 
@@ -554,6 +574,12 @@ void rt_timer_check(void)
             RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
             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) &&
                 (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
             {
@@ -561,14 +587,8 @@ void rt_timer_check(void)
                 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                 rt_timer_start(t);
             }
-            else
-            {
-                /* stop timer */
-                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
-            }
         }
-        else
-            break;
+        else break;
     }
 
     /* enable interrupt */
@@ -589,18 +609,20 @@ rt_tick_t rt_timer_next_timeout_tick(void)
 
 #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.
  */
 void rt_soft_timer_check(void)
 {
     rt_tick_t current_tick;
     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"));
 
-    /* lock scheduler */
-    rt_enter_critical();
+    /* disable interrupt */
+    level = rt_hw_interrupt_disable();
 
     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 */
             _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 */
             t->timeout_func(t->parameter);
 
             RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
             RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
 
-            /* lock scheduler */
-            rt_enter_critical();
+            /* disable interrupt */
+            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) &&
                 (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
@@ -638,17 +675,11 @@ void rt_soft_timer_check(void)
                 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                 rt_timer_start(t);
             }
-            else
-            {
-                /* stop timer */
-                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
-            }
         }
         else break; /* not check anymore */
     }
-
-    /* unlock scheduler */
-    rt_exit_critical();
+    /* enable interrupt */
+    rt_hw_interrupt_enable(level);
 
     RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
 }