waitqueue.c
4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018/06/26 Bernard Fix the wait queue issue when wakeup a soon
* to blocked thread.
* 2022-01-24 THEWON let rt_wqueue_wait return thread->error when using signal
*/
#include <stdint.h>
#include <rthw.h>
#include <rtdevice.h>
#include <rtservice.h>
/**
* @brief This function will insert a node to the wait queue.
*
* @param queue is a pointer to the wait queue.
*
* @param node is a pointer to the node to be inserted.
*/
void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node)
{
rt_base_t level;
level = rt_hw_interrupt_disable();
rt_list_insert_before(&(queue->waiting_list), &(node->list));
rt_hw_interrupt_enable(level);
}
/**
* @brief This function will remove a node from the wait queue.
*
* @param queue is a pointer to the wait queue.
*
* @param node is a pointer to the node to be removed.
*/
void rt_wqueue_remove(struct rt_wqueue_node *node)
{
rt_base_t level;
level = rt_hw_interrupt_disable();
rt_list_remove(&(node->list));
rt_hw_interrupt_enable(level);
}
/**
* @brief This function is the default wakeup function, but it doesn't do anything in actual.
* It always return 0, user should define their own wakeup function.
*
* @param queue is a pointer to the wait queue.
*
* @param key is the wakeup condition.
*
* @return always return 0.
*/
int __wqueue_default_wake(struct rt_wqueue_node *wait, void *key)
{
return 0;
}
/**
* @brief This function will wake up a pending thread on the specified waiting queue that meets the conditions.
*
* @param queue is a pointer to the wait queue.
*
* @param key is the wakeup conditions, but it is not effective now, because
* default wakeup function always return 0.
* If user wants to use it, user should define their own wakeup function.
*/
void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)
{
rt_base_t level;
register int need_schedule = 0;
rt_list_t *queue_list;
struct rt_list_node *node;
struct rt_wqueue_node *entry;
queue_list = &(queue->waiting_list);
level = rt_hw_interrupt_disable();
/* set wakeup flag in the queue */
queue->flag = RT_WQ_FLAG_WAKEUP;
if (!(rt_list_isempty(queue_list)))
{
for (node = queue_list->next; node != queue_list; node = node->next)
{
entry = rt_list_entry(node, struct rt_wqueue_node, list);
if (entry->wakeup(entry, key) == 0)
{
rt_thread_resume(entry->polling_thread);
need_schedule = 1;
rt_wqueue_remove(entry);
break;
}
}
}
rt_hw_interrupt_enable(level);
if (need_schedule)
rt_schedule();
}
/**
* @brief This function will join a thread to the specified waiting queue, the thread will holds a wait or
* timeout return on the specified wait queue.
*
* @param queue is a pointer to the wait queue.
*
* @param condition is parameters compatible with POSIX standard interface (currently meaningless, just pass in 0).
*
* @param msec is the timeout value, unit is millisecond.
*
* @return Return 0 if the thread is woken up.
*/
int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)
{
int tick;
rt_thread_t tid = rt_thread_self();
rt_timer_t tmr = &(tid->thread_timer);
struct rt_wqueue_node __wait;
rt_base_t level;
/* current context checking */
RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
tick = rt_tick_from_millisecond(msec);
if ((condition) || (tick == 0))
return 0;
__wait.polling_thread = rt_thread_self();
__wait.key = 0;
__wait.wakeup = __wqueue_default_wake;
rt_list_init(&__wait.list);
level = rt_hw_interrupt_disable();
/* reset thread error */
tid->error = RT_EOK;
if (queue->flag == RT_WQ_FLAG_WAKEUP)
{
/* already wakeup */
goto __exit_wakeup;
}
rt_wqueue_add(queue, &__wait);
rt_thread_suspend(tid);
/* start timer */
if (tick != RT_WAITING_FOREVER)
{
rt_timer_control(tmr,
RT_TIMER_CTRL_SET_TIME,
&tick);
rt_timer_start(tmr);
}
rt_hw_interrupt_enable(level);
rt_schedule();
level = rt_hw_interrupt_disable();
__exit_wakeup:
queue->flag = RT_WQ_FLAG_CLEAN;
rt_hw_interrupt_enable(level);
rt_wqueue_remove(&__wait);
return tid->error;
}