xref: /nrf52832-nimble/rt-thread/components/drivers/src/waitqueue.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018/06/26     Bernard      Fix the wait queue issue when wakeup a soon
9  *                             to blocked thread.
10  */
11 
12 #include <stdint.h>
13 
14 #include <rthw.h>
15 #include <rtdevice.h>
16 #include <rtservice.h>
17 
rt_wqueue_add(rt_wqueue_t * queue,struct rt_wqueue_node * node)18 void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node)
19 {
20     rt_base_t level;
21 
22     level = rt_hw_interrupt_disable();
23     rt_list_insert_before(&(queue->waiting_list), &(node->list));
24     rt_hw_interrupt_enable(level);
25 }
26 
rt_wqueue_remove(struct rt_wqueue_node * node)27 void rt_wqueue_remove(struct rt_wqueue_node *node)
28 {
29     rt_base_t level;
30 
31     level = rt_hw_interrupt_disable();
32     rt_list_remove(&(node->list));
33     rt_hw_interrupt_enable(level);
34 }
35 
__wqueue_default_wake(struct rt_wqueue_node * wait,void * key)36 int __wqueue_default_wake(struct rt_wqueue_node *wait, void *key)
37 {
38     return 0;
39 }
40 
rt_wqueue_wakeup(rt_wqueue_t * queue,void * key)41 void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)
42 {
43     rt_base_t level;
44     register int need_schedule = 0;
45 
46     rt_list_t *queue_list;
47     struct rt_list_node *node;
48     struct rt_wqueue_node *entry;
49 
50     queue_list = &(queue->waiting_list);
51 
52     level = rt_hw_interrupt_disable();
53     /* set wakeup flag in the queue */
54     queue->flag = RT_WQ_FLAG_WAKEUP;
55 
56     if (!(rt_list_isempty(queue_list)))
57     {
58         for (node = queue_list->next; node != queue_list; node = node->next)
59         {
60             entry = rt_list_entry(node, struct rt_wqueue_node, list);
61             if (entry->wakeup(entry, key) == 0)
62             {
63                 rt_thread_resume(entry->polling_thread);
64                 need_schedule = 1;
65 
66                 rt_wqueue_remove(entry);
67                 break;
68             }
69         }
70     }
71     rt_hw_interrupt_enable(level);
72 
73     if (need_schedule)
74         rt_schedule();
75 }
76 
rt_wqueue_wait(rt_wqueue_t * queue,int condition,int msec)77 int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)
78 {
79     int tick;
80     rt_thread_t tid = rt_thread_self();
81     rt_timer_t  tmr = &(tid->thread_timer);
82     struct rt_wqueue_node __wait;
83     rt_base_t level;
84 
85     /* current context checking */
86     RT_DEBUG_NOT_IN_INTERRUPT;
87 
88     tick = rt_tick_from_millisecond(msec);
89 
90     if ((condition) || (tick == 0))
91         return 0;
92 
93     __wait.polling_thread = rt_thread_self();
94     __wait.key = 0;
95     __wait.wakeup = __wqueue_default_wake;
96     rt_list_init(&__wait.list);
97 
98     level = rt_hw_interrupt_disable();
99     if (queue->flag == RT_WQ_FLAG_WAKEUP)
100     {
101         /* already wakeup */
102         goto __exit_wakeup;
103     }
104 
105     rt_wqueue_add(queue, &__wait);
106     rt_thread_suspend(tid);
107 
108     /* start timer */
109     if (tick != RT_WAITING_FOREVER)
110     {
111         rt_timer_control(tmr,
112                          RT_TIMER_CTRL_SET_TIME,
113                          &tick);
114 
115         rt_timer_start(tmr);
116     }
117     rt_hw_interrupt_enable(level);
118 
119     rt_schedule();
120 
121     level = rt_hw_interrupt_disable();
122 
123 __exit_wakeup:
124     queue->flag = 0;
125     rt_hw_interrupt_enable(level);
126 
127     rt_wqueue_remove(&__wait);
128 
129     return 0;
130 }
131