1 /*
2 * Copyright (c) 2009-2022 Huawei Technologies Co., Ltd. All rights reserved.
3 *
4 * UniProton is licensed under Mulan PSL v2.
5 * You can use this software according to the terms and conditions of the Mulan PSL v2.
6 * You may obtain a copy of Mulan PSL v2 at:
7 * http://license.coscl.org.cn/MulanPSL2
8 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11 * See the Mulan PSL v2 for more details.
12 * Create: 2009-12-22
13 * Description: 信号量模块
14 */
15 #include "prt_sem_external.h"
16 #include "prt_asm_cpu_external.h"
17
18 /* 核内信号量最大个数 */
19 OS_SEC_BSS U16 g_maxSem;
20
21 #if defined(OS_OPTION_BIN_SEM)
OsSemPostErrorCheck(struct TagSemCb * semPosted,SemHandle semHandle)22 OS_SEC_ALW_INLINE INLINE U32 OsSemPostErrorCheck(struct TagSemCb *semPosted, SemHandle semHandle)
23 {
24 (void)semHandle;
25 /* 检查信号量控制块是否UNUSED,排除大部分错误场景 */
26 if (semPosted->semStat == OS_SEM_UNUSED) {
27 return OS_ERRNO_SEM_INVALID;
28 }
29 if (GET_SEM_TYPE((semPosted)->semType) == SEM_TYPE_COUNT) {
30 /* 释放计数型信号量且信号量计数大于最大计数 */
31 if ((semPosted)->semCount >= OS_SEM_COUNT_MAX) {
32 return OS_ERRNO_SEM_OVERFLOW;
33 }
34 } else if (GET_SEM_TYPE(semPosted->semType) == SEM_TYPE_BIN) {
35 if (OS_INT_ACTIVE) {
36 return OS_ERRNO_SEM_MUTEX_POST_INTERR;
37 }
38
39 /* 如果不是 互斥信号量的持有任务来做post操作 */
40 if (semPosted->semOwner != RUNNING_TASK->taskPid) {
41 return OS_ERRNO_SEM_MUTEX_NOT_OWNER_POST;
42 }
43 }
44 return OS_OK;
45 }
46
OsSemPostBinMutex(struct TagSemCb * semPosted,struct TagTskCb * resumedTask)47 OS_SEC_ALW_INLINE INLINE void OsSemPostBinMutex(struct TagSemCb *semPosted, struct TagTskCb *resumedTask)
48 {
49 semPosted->semOwner = resumedTask->taskPid;
50 if (semPosted->semType == SEM_TYPE_BIN) {
51 ListDelete(&semPosted->semBList);
52 ListTailAdd(&semPosted->semBList, &resumedTask->semBList);
53 }
54 }
55 #else
OsSemPostErrorCheck(struct TagSemCb * semPosted,SemHandle semHandle)56 OS_SEC_ALW_INLINE INLINE U32 OsSemPostErrorCheck(struct TagSemCb *semPosted, SemHandle semHandle)
57 {
58 (void)semHandle;
59 /* 检查信号量控制块是否UNUSED,排除大部分错误场景 */
60 if (semPosted->semStat == OS_SEM_UNUSED) {
61 return OS_ERRNO_SEM_INVALID;
62 }
63
64 /* post计数型信号量的错误场景, 释放计数型信号量且信号量计数大于最大计数 */
65 if ((semPosted)->semCount >= OS_SEM_COUNT_MAX) {
66 return OS_ERRNO_SEM_OVERFLOW;
67 }
68
69 return OS_OK;
70 }
71 #endif
72
73 /*
74 * 描述:把当前运行任务挂接到信号量链表上
75 */
OsSemPendListPut(struct TagSemCb * semPended,U32 timeOut)76 OS_SEC_L0_TEXT void OsSemPendListPut(struct TagSemCb *semPended, U32 timeOut)
77 {
78 struct TagTskCb *curTskCb = NULL;
79 struct TagTskCb *runTsk = RUNNING_TASK;
80 struct TagListObject *pendObj = &runTsk->pendList;
81
82 OsTskReadyDel((struct TagTskCb *)runTsk);
83
84 runTsk->taskSem = (void *)semPended;
85
86 TSK_STATUS_SET(runTsk, OS_TSK_PEND);
87 /* 根据唤醒方式挂接此链表,同优先级再按FIFO子顺序插入 */
88 if (semPended->semMode == SEM_MODE_PRIOR) {
89 LIST_FOR_EACH(curTskCb, &semPended->semList, struct TagTskCb, pendList) {
90 if (curTskCb->priority > runTsk->priority) {
91 ListTailAdd(pendObj, &curTskCb->pendList);
92 goto TIMER_ADD;
93 }
94 }
95 }
96 /* 如果到这里,说明是FIFO方式;或者是优先级方式且挂接首个节点或者挂接尾节点 */
97 ListTailAdd(pendObj, &semPended->semList);
98 TIMER_ADD:
99 // timer超时链表添加
100 if (timeOut != OS_WAIT_FOREVER) {
101 /* 如果不是永久等待则将任务挂到计时器链表中,设置OS_TSK_TIMEOUT是为了判断是否等待超时 */
102 OsTskTimerAdd((struct TagTskCb *)runTsk, timeOut);
103
104 TSK_STATUS_SET(runTsk, OS_TSK_TIMEOUT);
105 }
106 }
107
108 /*
109 * 描述:从非空信号量链表上摘首个任务放入到ready队列
110 */
OsSemPendListGet(struct TagSemCb * semPended)111 OS_SEC_L0_TEXT struct TagTskCb *OsSemPendListGet(struct TagSemCb *semPended)
112 {
113 struct TagTskCb *taskCb = GET_TCB_PEND(LIST_FIRST(&(semPended->semList)));
114
115 ListDelete(LIST_FIRST(&(semPended->semList)));
116 /* 如果阻塞的任务属于定时等待的任务时候,去掉其定时等待标志位,并将其从去除 */
117 if (TSK_STATUS_TST(taskCb, OS_TSK_TIMEOUT)) {
118 OS_TSK_DELAY_LOCKED_DETACH(taskCb);
119 }
120
121 /* 必须先去除 OS_TSK_TIMEOUT 态,再入队[睡眠时是先出ready队,再置OS_TSK_TIMEOUT态] */
122 TSK_STATUS_CLEAR(taskCb, OS_TSK_TIMEOUT | OS_TSK_PEND);
123 taskCb->taskSem = NULL;
124 /* 如果去除信号量阻塞位后,该任务不处于阻塞态则将该任务挂入就绪队列并触发任务调度 */
125 if (!TSK_STATUS_TST(taskCb, OS_TSK_SUSPEND)) {
126 OsTskReadyAddBgd(taskCb);
127 }
128
129 #if defined(OS_OPTION_BIN_SEM)
130 OsSemPostBinMutex(semPended, taskCb);
131 #endif
132 return taskCb;
133 }
134
OsSemPendParaCheck(U32 timeout)135 OS_SEC_L0_TEXT U32 OsSemPendParaCheck(U32 timeout)
136 {
137 if (timeout == 0) {
138 return OS_ERRNO_SEM_UNAVAILABLE;
139 }
140
141 if (OS_TASK_LOCK_DATA != 0) {
142 return OS_ERRNO_SEM_PEND_IN_LOCK;
143 }
144 return OS_OK;
145 }
146
OsSemPendNotNeedSche(struct TagSemCb * semPended,struct TagTskCb * runTsk)147 OS_SEC_L0_TEXT bool OsSemPendNotNeedSche(struct TagSemCb *semPended, struct TagTskCb *runTsk)
148 {
149 #if defined(OS_OPTION_SEM_RECUR_PV)
150 if (GET_SEM_TYPE(semPended->semType) == SEM_TYPE_BIN && semPended->semOwner == runTsk->taskPid &&
151 (GET_MUTEX_TYPE(semPended->semType) == PTHREAD_MUTEX_RECURSIVE)) {
152 semPended->recurCount++;
153 return TRUE;
154 }
155 #endif
156
157 if (semPended->semCount > 0) {
158 semPended->semCount--;
159 semPended->semOwner = runTsk->taskPid;
160 #if defined(OS_OPTION_BIN_SEM)
161 /* 如果是互斥信号量,把持有的互斥信号量挂接起来 */
162 if (GET_SEM_TYPE(semPended->semType) == SEM_TYPE_BIN) {
163 ListTailAdd(&semPended->semBList, &runTsk->semBList);
164 }
165 #endif
166 return TRUE;
167 }
168 return FALSE;
169 }
170
171 /*
172 * 描述:指定信号量的P操作
173 */
PRT_SemPend(SemHandle semHandle,U32 timeout)174 OS_SEC_L0_TEXT U32 PRT_SemPend(SemHandle semHandle, U32 timeout)
175 {
176 uintptr_t intSave;
177 U32 ret;
178 struct TagTskCb *runTsk = NULL;
179 struct TagSemCb *semPended = NULL;
180
181 if (semHandle >= (SemHandle)g_maxSem) {
182 return OS_ERRNO_SEM_INVALID;
183 }
184
185 semPended = GET_SEM(semHandle);
186
187 intSave = OsIntLock();
188 if (semPended->semStat == OS_SEM_UNUSED) {
189 OsIntRestore(intSave);
190 return OS_ERRNO_SEM_INVALID;
191 }
192
193 if (OS_INT_ACTIVE) {
194 OsIntRestore(intSave);
195 return OS_ERRNO_SEM_PEND_INTERR;
196 }
197
198 runTsk = (struct TagTskCb *)RUNNING_TASK;
199
200 if (OsSemPendNotNeedSche(semPended, runTsk) == TRUE) {
201 OsIntRestore(intSave);
202 return OS_OK;
203 }
204
205 ret = OsSemPendParaCheck(timeout);
206 if (ret != OS_OK) {
207 OsIntRestore(intSave);
208 return ret;
209 }
210 /* 把当前任务挂接在信号量链表上 */
211 OsSemPendListPut(semPended, timeout);
212 if (timeout != OS_WAIT_FOREVER) {
213 /* 触发任务调度 */
214 OsTskSchedule();
215
216 /* 判断是否是等待信号量超时 */
217 if (TSK_STATUS_TST(runTsk, OS_TSK_TIMEOUT)) {
218 TSK_STATUS_CLEAR(runTsk, OS_TSK_TIMEOUT);
219 OsIntRestore(intSave);
220 return OS_ERRNO_SEM_TIMEOUT;
221 }
222 } else {
223 /* 恢复ps的快速切换 */
224 OsTskScheduleFastPs(intSave);
225 }
226
227 OsIntRestore(intSave);
228 return OS_OK;
229 }
230
OsSemPostSchePre(struct TagSemCb * semPosted)231 OS_SEC_ALW_INLINE INLINE void OsSemPostSchePre(struct TagSemCb *semPosted)
232 {
233 struct TagTskCb *resumedTask = NULL;
234
235 resumedTask = OsSemPendListGet(semPosted);
236 semPosted->semOwner = resumedTask->taskPid;
237 #if defined(OS_OPTION_BIN_SEM)
238 /*
239 * 如果释放的是互斥信号量,就从释放此互斥信号量任务的持有链表上摘除它,
240 * 再把它挂接到新的持有它的任务的持有链表上;然后尝试降低任务的优先级
241 */
242 if (GET_SEM_TYPE(semPosted->semType) == SEM_TYPE_BIN) {
243 ListDelete(&semPosted->semBList);
244 ListTailAdd(&semPosted->semBList, &resumedTask->semBList);
245 }
246 #endif
247 }
248
249 /*
250 * 描述:判断信号量post是否有效
251 * 备注:以下情况表示post无效,返回TRUE: post互斥二进制信号量,若该信号量被嵌套pend或者已处于空闲状态
252 */
OsSemPostIsInvalid(struct TagSemCb * semPosted)253 OS_SEC_ALW_INLINE INLINE bool OsSemPostIsInvalid(struct TagSemCb *semPosted)
254 {
255 if (GET_SEM_TYPE(semPosted->semType) == SEM_TYPE_BIN) {
256 #if defined(OS_OPTION_SEM_RECUR_PV)
257 if ((GET_MUTEX_TYPE(semPosted->semType) == PTHREAD_MUTEX_RECURSIVE) && semPosted->recurCount > 0) {
258 semPosted->recurCount--;
259 return TRUE;
260 }
261 #endif
262 /* 释放互斥二进制信号量且信号量已处于空闲状态 */
263 if ((semPosted)->semCount == OS_SEM_FULL) {
264 return TRUE;
265 }
266 }
267 return FALSE;
268 }
269
270 /*
271 * 描述:指定信号量的V操作
272 */
PRT_SemPost(SemHandle semHandle)273 OS_SEC_L0_TEXT U32 PRT_SemPost(SemHandle semHandle)
274 {
275 U32 ret;
276 uintptr_t intSave;
277 struct TagSemCb *semPosted = NULL;
278
279 if (semHandle >= (SemHandle)g_maxSem) {
280 return OS_ERRNO_SEM_INVALID;
281 }
282
283 semPosted = GET_SEM(semHandle);
284 intSave = OsIntLock();
285
286 ret = OsSemPostErrorCheck(semPosted, semHandle);
287 if (ret != OS_OK) {
288 OsIntRestore(intSave);
289 return ret;
290 }
291
292 /* 信号量post无效,不需要调度 */
293 if (OsSemPostIsInvalid(semPosted) == TRUE) {
294 OsIntRestore(intSave);
295 return OS_OK;
296 }
297
298 /* 如果有任务阻塞在信号量上,就激活信号量阻塞队列上的首个任务 */
299 if (!ListEmpty(&semPosted->semList)) {
300 OsSemPostSchePre(semPosted);
301 /* 相当于快速切换+中断恢复 */
302 OsTskScheduleFastPs(intSave);
303 } else {
304 semPosted->semCount++;
305 semPosted->semOwner = OS_INVALID_OWNER_ID;
306 #if defined(OS_OPTION_BIN_SEM)
307 /* 如果释放的是互斥信号量,就从释放此互斥信号量任务的持有链表上摘除它 */
308 if (GET_SEM_TYPE(semPosted->semType) == SEM_TYPE_BIN) {
309 ListDelete(&semPosted->semBList);
310 }
311 #endif
312 }
313
314 OsIntRestore(intSave);
315 return OS_OK;
316 }
317