• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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