• 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 /*
22  * 描述:把当前运行任务挂接到信号量链表上
23  */
OsSemPendListPut(struct TagSemCb * semPended,U32 timeOut)24 OS_SEC_L0_TEXT void OsSemPendListPut(struct TagSemCb *semPended, U32 timeOut)
25 {
26     struct TagTskCb *curTskCb = NULL;
27     struct TagTskCb *runTsk = RUNNING_TASK;
28     struct TagListObject *pendObj = &runTsk->pendList;
29 
30     OsTskReadyDel((struct TagTskCb *)runTsk);
31 
32     runTsk->taskSem = (void *)semPended;
33 
34     TSK_STATUS_SET(runTsk, OS_TSK_PEND);
35     /* 根据唤醒方式挂接此链表,同优先级再按FIFO子顺序插入 */
36     if (semPended->semMode == SEM_MODE_PRIOR) {
37         LIST_FOR_EACH(curTskCb, &semPended->semList, struct TagTskCb, pendList) {
38             if (curTskCb->priority > runTsk->priority) {
39                 ListTailAdd(pendObj, &curTskCb->pendList);
40                 goto TIMER_ADD;
41             }
42         }
43     }
44     /* 如果到这里,说明是FIFO方式;或者是优先级方式且挂接首个节点或者挂接尾节点 */
45     ListTailAdd(pendObj, &semPended->semList);
46 TIMER_ADD:
47     // timer超时链表添加
48     if (timeOut != OS_WAIT_FOREVER) {
49         /* 如果不是永久等待则将任务挂到计时器链表中,设置OS_TSK_TIMEOUT是为了判断是否等待超时 */
50         OsTskTimerAdd((struct TagTskCb *)runTsk, timeOut);
51 
52         TSK_STATUS_SET(runTsk, OS_TSK_TIMEOUT);
53     }
54 
55 }
56 
57 /*
58  * 描述:从非空信号量链表上摘首个任务放入到ready队列
59  */
OsSemPendListGet(struct TagSemCb * semPended)60 OS_SEC_L0_TEXT struct TagTskCb *OsSemPendListGet(struct TagSemCb *semPended)
61 {
62     struct TagTskCb *taskCb = GET_TCB_PEND(LIST_FIRST(&(semPended->semList)));
63 
64     ListDelete(LIST_FIRST(&(semPended->semList)));
65     /* 如果阻塞的任务属于定时等待的任务时候,去掉其定时等待标志位,并将其从去除 */
66     if (TSK_STATUS_TST(taskCb, OS_TSK_TIMEOUT)) {
67         OS_TSK_DELAY_LOCKED_DETACH(taskCb);
68     }
69 
70     // 必须先去除 OS_TSK_TIMEOUT 态,再入队[睡眠时是先出ready队,再置OS_TSK_TIMEOUT态]
71     TSK_STATUS_CLEAR(taskCb, OS_TSK_TIMEOUT | OS_TSK_PEND);
72     taskCb->taskSem = NULL;
73     /* 如果去除信号量阻塞位后,该任务不处于阻塞态则将该任务挂入就绪队列并触发任务调度 */
74     if (!TSK_STATUS_TST(taskCb, OS_TSK_SUSPEND)) {
75         OsTskReadyAddBgd(taskCb);
76     }
77 
78     return taskCb;
79 }
80 
OsSemPendParaCheck(U32 timeout)81 OS_SEC_L0_TEXT U32 OsSemPendParaCheck(U32 timeout)
82 {
83     if (timeout == 0) {
84         return OS_ERRNO_SEM_UNAVAILABLE;
85     }
86 
87     /* 如果锁任务的情况下 */
88     if (OS_TASK_LOCK_DATA != 0) {
89         return OS_ERRNO_SEM_PEND_IN_LOCK;
90     }
91     return OS_OK;
92 }
93 
OsSemPendNotNeedSche(struct TagSemCb * semPended,struct TagTskCb * runTsk)94 OS_SEC_L0_TEXT bool OsSemPendNotNeedSche(struct TagSemCb *semPended, struct TagTskCb *runTsk)
95 {
96     // 信号量获取成功
97     if (semPended->semCount > 0) {
98         semPended->semCount--;
99         semPended->semOwner = runTsk->taskPid;
100 
101         return TRUE;
102     }
103     return FALSE;
104 }
105 
106 /*
107  * 描述:指定信号量的P操作
108  */
PRT_SemPend(SemHandle semHandle,U32 timeout)109 OS_SEC_L0_TEXT U32 PRT_SemPend(SemHandle semHandle, U32 timeout)
110 {
111     uintptr_t intSave;
112     U32 ret;
113     struct TagTskCb *runTsk = NULL;
114     struct TagSemCb *semPended = NULL;
115 
116     if (semHandle >= (SemHandle)g_maxSem) {
117         return OS_ERRNO_SEM_INVALID;
118     }
119 
120     semPended = GET_SEM(semHandle);
121 
122     intSave = OsIntLock();
123     if (semPended->semStat == OS_SEM_UNUSED) {
124         OsIntRestore(intSave);
125         return OS_ERRNO_SEM_INVALID;
126     }
127 
128     /* 如果是中断的情况 */
129     if (OS_INT_ACTIVE) {
130         OsIntRestore(intSave);
131         return OS_ERRNO_SEM_PEND_INTERR;
132     }
133 
134     runTsk = (struct TagTskCb *)RUNNING_TASK;
135 
136     if (OsSemPendNotNeedSche(semPended, runTsk) == TRUE) {
137         OsIntRestore(intSave);
138         return OS_OK;
139     }
140 
141     ret = OsSemPendParaCheck(timeout);
142     if (ret != OS_OK) {
143         OsIntRestore(intSave);
144         return ret;
145     }
146     /* 把当前任务挂接在信号量链表上 */
147     OsSemPendListPut(semPended, timeout);
148     if (timeout != OS_WAIT_FOREVER) {
149         /* 触发任务调度 */
150         OsTskSchedule();
151 
152         /* 判断是否是等待信号量超时 */
153         if (TSK_STATUS_TST(runTsk, OS_TSK_TIMEOUT)) {
154             TSK_STATUS_CLEAR(runTsk, OS_TSK_TIMEOUT);
155             OsIntRestore(intSave);
156             return OS_ERRNO_SEM_TIMEOUT;
157         }
158     } else {
159         /* 恢复ps的快速切换 */
160         OsTskScheduleFastPs(intSave);
161     }
162 
163     OsIntRestore(intSave);
164     return OS_OK;
165 }
166 
OsSemPostSchePre(struct TagSemCb * semPosted)167 OS_SEC_ALW_INLINE INLINE void OsSemPostSchePre(struct TagSemCb *semPosted)
168 {
169     struct TagTskCb *resumedTask = NULL;
170 
171     resumedTask = OsSemPendListGet(semPosted);
172     semPosted->semOwner = resumedTask->taskPid;
173 }
174 
OsSemPostErrorCheck(struct TagSemCb * semPosted,SemHandle semHandle)175 OS_SEC_ALW_INLINE INLINE U32 OsSemPostErrorCheck(struct TagSemCb *semPosted, SemHandle semHandle)
176 {
177     (void)semHandle;
178     /* 检查信号量控制块是否UNUSED,排除大部分错误场景 */
179     if (semPosted->semStat == OS_SEM_UNUSED) {
180         return OS_ERRNO_SEM_INVALID;
181     }
182 
183     /* post计数型信号量的错误场景, 释放计数型信号量且信号量计数大于最大计数 */
184     if ((semPosted)->maxSemCount <= (semPosted)->semCount) {
185         return OS_ERRNO_SEM_OVERFLOW;
186     }
187     /* post同步二进制信号量或者不存在上述错误场景 */
188     return OS_OK;
189 }
190 
191 /*
192  * 描述:指定信号量的V操作
193  */
PRT_SemPost(SemHandle semHandle)194 OS_SEC_L0_TEXT U32 PRT_SemPost(SemHandle semHandle)
195 {
196     U32 ret;
197     uintptr_t intSave;
198     struct TagSemCb *semPosted = NULL;
199 
200     if (semHandle >= (SemHandle)g_maxSem) {
201         return OS_ERRNO_SEM_INVALID;
202     }
203 
204     semPosted = GET_SEM(semHandle);
205     intSave = OsIntLock();
206     /* 检查post信号量时的错误场景 */
207     ret = OsSemPostErrorCheck(semPosted, semHandle);
208     if (ret != OS_OK) {
209         OsIntRestore(intSave);
210         return ret;
211     }
212 
213     /* 如果有任务阻塞在信号量上,就激活信号量阻塞队列上的首个任务 */
214     if (!ListEmpty(&semPosted->semList)) {
215         OsSemPostSchePre(semPosted);
216         /* 相当于快速切换+中断恢复 */
217         OsTskScheduleFastPs(intSave);
218     } else {
219         /* 释放信号量,把持有信号量的任务ID设置为OS_THREAD_ID_INVALID */
220         semPosted->semCount++;
221         semPosted->semOwner = OS_INVALID_OWNER_ID;
222     }
223 
224     OsIntRestore(intSave);
225     return OS_OK;
226 }
227