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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-09-30 Bernard first version.
* 2021-08-18 chenyingchun add comments
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2024-01-25 Shell reduce resource usage in completion for better synchronization
* and smaller footprint.
*/
#define DBG_TAG "drivers.ipc"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <rthw.h>
#include <rtdevice.h>
#define RT_COMPLETED 1
#define RT_UNCOMPLETED 0
#define RT_COMPLETION_FLAG(comp) ((comp)->susp_thread_n_flag & 1)
#define RT_COMPLETION_THREAD(comp) ((rt_thread_t)((comp)->susp_thread_n_flag & ~1))
#define RT_COMPLETION_NEW_STAT(thread, flag) (((flag) & 1) | (((rt_base_t)thread) & ~1))
static struct rt_spinlock _completion_lock = RT_SPINLOCK_INIT;
/**
* @brief This function will initialize a completion object.
*
* @param completion is a pointer to a completion object.
*/
void rt_completion_init(struct rt_completion *completion)
{
RT_ASSERT(completion != RT_NULL);
completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(RT_NULL, RT_UNCOMPLETED);
}
RTM_EXPORT(rt_completion_init);
/**
* @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for
* the completion up to a specified time.
*
* @param completion is a pointer to a completion object.
*
* @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for
* the completion done up to the amount of time specified by the argument.
* NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the
* completion is unavailable, the thread will be waitting forever.
*
* @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful.
* If the return value is any other values, it means that the completion wait failed.
*
* @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context.
*/
rt_err_t rt_completion_wait(struct rt_completion *completion,
rt_int32_t timeout)
{
rt_err_t result;
rt_base_t level;
rt_thread_t thread;
RT_ASSERT(completion != RT_NULL);
/* current context checking */
RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
result = RT_EOK;
thread = rt_thread_self();
level = rt_spin_lock_irqsave(&_completion_lock);
if (RT_COMPLETION_FLAG(completion) != RT_COMPLETED)
{
/* only one thread can suspend on complete */
RT_ASSERT(RT_COMPLETION_THREAD(completion) == RT_NULL);
if (timeout == 0)
{
result = -RT_ETIMEOUT;
goto __exit;
}
else
{
/* reset thread error number */
thread->error = RT_EOK;
/* suspend thread */
result = rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
if (result == RT_EOK)
{
/* add to suspended thread */
completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(thread, RT_UNCOMPLETED);
/* current context checking */
RT_DEBUG_NOT_IN_INTERRUPT;
/* start timer */
if (timeout > 0)
{
/* reset the timeout of thread timer and start it */
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&timeout);
rt_timer_start(&(thread->thread_timer));
}
/* enable interrupt */
rt_spin_unlock_irqrestore(&_completion_lock, level);
/* do schedule */
rt_schedule();
/* thread is waked up */
result = thread->error;
level = rt_spin_lock_irqsave(&_completion_lock);
}
}
}
/* clean completed flag & remove susp_thread on the case of waking by timeout */
completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(RT_NULL, RT_UNCOMPLETED);
__exit:
rt_spin_unlock_irqrestore(&_completion_lock, level);
return result;
}
RTM_EXPORT(rt_completion_wait);
/**
* @brief This function indicates a completion has done.
*
* @param completion is a pointer to a completion object.
*/
static int _completion_done(struct rt_completion *completion)
{
rt_base_t level;
rt_err_t error;
rt_thread_t suspend_thread;
RT_ASSERT(completion != RT_NULL);
level = rt_spin_lock_irqsave(&_completion_lock);
if (RT_COMPLETION_FLAG(completion) == RT_COMPLETED)
{
rt_spin_unlock_irqrestore(&_completion_lock, level);
return -RT_EBUSY;
}
suspend_thread = RT_COMPLETION_THREAD(completion);
if (suspend_thread)
{
/* there is one thread in suspended list */
/* resume it */
error = rt_thread_resume(suspend_thread);
if (error)
{
LOG_D("%s: failed to resume thread", __func__);
}
}
else
{
/* no thread waiting */
error = -RT_EEMPTY;
}
completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(RT_NULL, RT_COMPLETED);
rt_spin_unlock_irqrestore(&_completion_lock, level);
return error;
}
/**
* @brief This function indicates a completion has done.
*
* @param completion is a pointer to a completion object.
*/
void rt_completion_done(struct rt_completion *completion)
{
_completion_done(completion);
}
RTM_EXPORT(rt_completion_done);
/**
* @brief This function indicates a completion has done and wakeup the thread
*
* @param completion is a pointer to a completion object.
*/
rt_err_t rt_completion_wakeup(struct rt_completion *completion)
{
return _completion_done(completion);
}
RTM_EXPORT(rt_completion_wakeup);
|