• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3  * Copyright (c) 2020-2022 Huawei Device Co., Ltd. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this list of
9  *    conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12  *    of conditions and the following disclaimer in the documentation and/or other materials
13  *    provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16  *    to endorse or promote products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "los_rwlock_pri.h"
33 #include "stdint.h"
34 #include "los_spinlock.h"
35 #include "los_mp.h"
36 #include "los_task_pri.h"
37 #include "los_exc.h"
38 #include "los_sched_pri.h"
39 
40 #ifdef LOSCFG_BASE_IPC_RWLOCK
41 #define RWLOCK_COUNT_MASK 0x00FFFFFFU
42 
LOS_RwlockIsValid(const LosRwlock * rwlock)43 BOOL LOS_RwlockIsValid(const LosRwlock *rwlock)
44 {
45     if ((rwlock != NULL) && ((rwlock->magic & RWLOCK_COUNT_MASK) == OS_RWLOCK_MAGIC)) {
46         return TRUE;
47     }
48 
49     return FALSE;
50 }
51 
LOS_RwlockInit(LosRwlock * rwlock)52 UINT32 LOS_RwlockInit(LosRwlock *rwlock)
53 {
54     UINT32 intSave;
55 
56     if (rwlock == NULL) {
57         return LOS_EINVAL;
58     }
59 
60     SCHEDULER_LOCK(intSave);
61     if ((rwlock->magic & RWLOCK_COUNT_MASK) == OS_RWLOCK_MAGIC) {
62         SCHEDULER_UNLOCK(intSave);
63         return LOS_EPERM;
64     }
65 
66     rwlock->rwCount = 0;
67     rwlock->writeOwner = NULL;
68     LOS_ListInit(&(rwlock->readList));
69     LOS_ListInit(&(rwlock->writeList));
70     rwlock->magic = OS_RWLOCK_MAGIC;
71     SCHEDULER_UNLOCK(intSave);
72     return LOS_OK;
73 }
74 
LOS_RwlockDestroy(LosRwlock * rwlock)75 UINT32 LOS_RwlockDestroy(LosRwlock *rwlock)
76 {
77     UINT32 intSave;
78 
79     if (rwlock == NULL) {
80         return LOS_EINVAL;
81     }
82 
83     SCHEDULER_LOCK(intSave);
84     if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
85         SCHEDULER_UNLOCK(intSave);
86         return LOS_EBADF;
87     }
88 
89     if (rwlock->rwCount != 0) {
90         SCHEDULER_UNLOCK(intSave);
91         return LOS_EBUSY;
92     }
93 
94     (VOID)memset_s(rwlock, sizeof(LosRwlock), 0, sizeof(LosRwlock));
95     SCHEDULER_UNLOCK(intSave);
96     return LOS_OK;
97 }
98 
OsRwlockCheck(const LosRwlock * rwlock)99 STATIC UINT32 OsRwlockCheck(const LosRwlock *rwlock)
100 {
101     if (rwlock == NULL) {
102         return LOS_EINVAL;
103     }
104 
105     if (OS_INT_ACTIVE) {
106         return LOS_EINTR;
107     }
108 
109     /* DO NOT Call blocking API in system tasks */
110     LosTaskCB *runTask = (LosTaskCB *)OsCurrTaskGet();
111     if (runTask->taskStatus & OS_TASK_FLAG_SYSTEM_TASK) {
112         return LOS_EPERM;
113     }
114 
115     return LOS_OK;
116 }
117 
OsRwlockPriCompare(LosTaskCB * runTask,LOS_DL_LIST * rwList)118 STATIC BOOL OsRwlockPriCompare(LosTaskCB *runTask, LOS_DL_LIST *rwList)
119 {
120     if (!LOS_ListEmpty(rwList)) {
121         LosTaskCB *highestTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(rwList));
122         if (OsSchedParamCompare(runTask, highestTask) < 0) {
123             return TRUE;
124         }
125         return FALSE;
126     }
127     return TRUE;
128 }
129 
OsRwlockRdPendOp(LosTaskCB * runTask,LosRwlock * rwlock,UINT32 timeout)130 STATIC UINT32 OsRwlockRdPendOp(LosTaskCB *runTask, LosRwlock *rwlock, UINT32 timeout)
131 {
132     UINT32 ret;
133 
134     /*
135      * When the rwlock mode is read mode or free mode and the priority of the current read task
136      * is higher than the first pended write task. current read task can obtain this rwlock.
137      */
138     if (rwlock->rwCount >= 0) {
139         if (OsRwlockPriCompare(runTask, &(rwlock->writeList))) {
140             if (rwlock->rwCount == INT8_MAX) {
141                 return LOS_EINVAL;
142             }
143             rwlock->rwCount++;
144             return LOS_OK;
145         }
146     }
147 
148     if (!timeout) {
149         return LOS_EINVAL;
150     }
151 
152     if (!OsPreemptableInSched()) {
153         return LOS_EDEADLK;
154     }
155 
156     /* The current task is not allowed to obtain the write lock when it obtains the read lock. */
157     if ((LosTaskCB *)(rwlock->writeOwner) == runTask) {
158         return LOS_EINVAL;
159     }
160 
161     /*
162      * When the rwlock mode is write mode or the priority of the current read task
163      * is lower than the first pended write task, current read task will be pended.
164      */
165     LOS_DL_LIST *node = OsSchedLockPendFindPos(runTask, &(rwlock->readList));
166     ret = runTask->ops->wait(runTask, node, timeout);
167     if (ret == LOS_ERRNO_TSK_TIMEOUT) {
168         return LOS_ETIMEDOUT;
169     }
170 
171     return ret;
172 }
173 
OsRwlockWrPendOp(LosTaskCB * runTask,LosRwlock * rwlock,UINT32 timeout)174 STATIC UINT32 OsRwlockWrPendOp(LosTaskCB *runTask, LosRwlock *rwlock, UINT32 timeout)
175 {
176     UINT32 ret;
177 
178     /* When the rwlock is free mode, current write task can obtain this rwlock. */
179     if (rwlock->rwCount == 0) {
180         rwlock->rwCount = -1;
181         rwlock->writeOwner = (VOID *)runTask;
182         return LOS_OK;
183     }
184 
185     /* Current write task can use one rwlock once again if the rwlock owner is it. */
186     if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) == runTask)) {
187         if (rwlock->rwCount == INT8_MIN) {
188             return LOS_EINVAL;
189         }
190         rwlock->rwCount--;
191         return LOS_OK;
192     }
193 
194     if (!timeout) {
195         return LOS_EINVAL;
196     }
197 
198     if (!OsPreemptableInSched()) {
199         return LOS_EDEADLK;
200     }
201 
202     /*
203      * When the rwlock is read mode or other write task obtains this rwlock, current
204      * write task will be pended.
205      */
206     LOS_DL_LIST *node =  OsSchedLockPendFindPos(runTask, &(rwlock->writeList));
207     ret = runTask->ops->wait(runTask, node, timeout);
208     if (ret == LOS_ERRNO_TSK_TIMEOUT) {
209         ret = LOS_ETIMEDOUT;
210     }
211 
212     return ret;
213 }
214 
OsRwlockRdUnsafe(LosRwlock * rwlock,UINT32 timeout)215 UINT32 OsRwlockRdUnsafe(LosRwlock *rwlock, UINT32 timeout)
216 {
217     if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
218         return LOS_EBADF;
219     }
220 
221     return OsRwlockRdPendOp(OsCurrTaskGet(), rwlock, timeout);
222 }
223 
OsRwlockTryRdUnsafe(LosRwlock * rwlock,UINT32 timeout)224 UINT32 OsRwlockTryRdUnsafe(LosRwlock *rwlock, UINT32 timeout)
225 {
226     if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
227         return LOS_EBADF;
228     }
229 
230     LosTaskCB *runTask = OsCurrTaskGet();
231     if ((LosTaskCB *)(rwlock->writeOwner) == runTask) {
232         return LOS_EINVAL;
233     }
234 
235     /*
236      * When the rwlock mode is read mode or free mode and the priority of the current read task
237      * is lower than the first pended write task, current read task can not obtain the rwlock.
238      */
239     if ((rwlock->rwCount >= 0) && !OsRwlockPriCompare(runTask, &(rwlock->writeList))) {
240         return LOS_EBUSY;
241     }
242 
243     /*
244      * When the rwlock mode is write mode, current read task can not obtain the rwlock.
245      */
246     if (rwlock->rwCount < 0) {
247         return LOS_EBUSY;
248     }
249 
250     return OsRwlockRdPendOp(runTask, rwlock, timeout);
251 }
252 
OsRwlockWrUnsafe(LosRwlock * rwlock,UINT32 timeout)253 UINT32 OsRwlockWrUnsafe(LosRwlock *rwlock, UINT32 timeout)
254 {
255     if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
256         return LOS_EBADF;
257     }
258 
259     return OsRwlockWrPendOp(OsCurrTaskGet(), rwlock, timeout);
260 }
261 
OsRwlockTryWrUnsafe(LosRwlock * rwlock,UINT32 timeout)262 UINT32 OsRwlockTryWrUnsafe(LosRwlock *rwlock, UINT32 timeout)
263 {
264     if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
265         return LOS_EBADF;
266     }
267 
268     /* When the rwlock is read mode, current write task will be pended. */
269     if (rwlock->rwCount > 0) {
270         return LOS_EBUSY;
271     }
272 
273     /* When other write task obtains this rwlock, current write task will be pended. */
274     LosTaskCB *runTask = OsCurrTaskGet();
275     if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) != runTask)) {
276         return LOS_EBUSY;
277     }
278 
279     return OsRwlockWrPendOp(runTask, rwlock, timeout);
280 }
281 
LOS_RwlockRdLock(LosRwlock * rwlock,UINT32 timeout)282 UINT32 LOS_RwlockRdLock(LosRwlock *rwlock, UINT32 timeout)
283 {
284     UINT32 intSave;
285 
286     UINT32 ret = OsRwlockCheck(rwlock);
287     if (ret != LOS_OK) {
288         return ret;
289     }
290 
291     SCHEDULER_LOCK(intSave);
292     ret = OsRwlockRdUnsafe(rwlock, timeout);
293     SCHEDULER_UNLOCK(intSave);
294     return ret;
295 }
296 
LOS_RwlockTryRdLock(LosRwlock * rwlock)297 UINT32 LOS_RwlockTryRdLock(LosRwlock *rwlock)
298 {
299     UINT32 intSave;
300 
301     UINT32 ret = OsRwlockCheck(rwlock);
302     if (ret != LOS_OK) {
303         return ret;
304     }
305 
306     SCHEDULER_LOCK(intSave);
307     ret = OsRwlockTryRdUnsafe(rwlock, 0);
308     SCHEDULER_UNLOCK(intSave);
309     return ret;
310 }
311 
LOS_RwlockWrLock(LosRwlock * rwlock,UINT32 timeout)312 UINT32 LOS_RwlockWrLock(LosRwlock *rwlock, UINT32 timeout)
313 {
314     UINT32 intSave;
315 
316     UINT32 ret = OsRwlockCheck(rwlock);
317     if (ret != LOS_OK) {
318         return ret;
319     }
320 
321     SCHEDULER_LOCK(intSave);
322     ret = OsRwlockWrUnsafe(rwlock, timeout);
323     SCHEDULER_UNLOCK(intSave);
324     return ret;
325 }
326 
LOS_RwlockTryWrLock(LosRwlock * rwlock)327 UINT32 LOS_RwlockTryWrLock(LosRwlock *rwlock)
328 {
329     UINT32 intSave;
330 
331     UINT32 ret = OsRwlockCheck(rwlock);
332     if (ret != LOS_OK) {
333         return ret;
334     }
335 
336     SCHEDULER_LOCK(intSave);
337     ret = OsRwlockTryWrUnsafe(rwlock, 0);
338     SCHEDULER_UNLOCK(intSave);
339     return ret;
340 }
341 
OsRwlockGetMode(LOS_DL_LIST * readList,LOS_DL_LIST * writeList)342 STATIC UINT32 OsRwlockGetMode(LOS_DL_LIST *readList, LOS_DL_LIST *writeList)
343 {
344     BOOL isReadEmpty = LOS_ListEmpty(readList);
345     BOOL isWriteEmpty = LOS_ListEmpty(writeList);
346     if (isReadEmpty && isWriteEmpty) {
347         return RWLOCK_NONE_MODE;
348     }
349     if (!isReadEmpty && isWriteEmpty) {
350         return RWLOCK_READ_MODE;
351     }
352     if (isReadEmpty && !isWriteEmpty) {
353         return RWLOCK_WRITE_MODE;
354     }
355     LosTaskCB *pendedReadTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(readList));
356     LosTaskCB *pendedWriteTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(writeList));
357     if (OsSchedParamCompare(pendedWriteTask, pendedReadTask) <= 0) {
358         return RWLOCK_WRITEFIRST_MODE;
359     }
360     return RWLOCK_READFIRST_MODE;
361 }
362 
OsRwlockPostOp(LosRwlock * rwlock,BOOL * needSched)363 STATIC UINT32 OsRwlockPostOp(LosRwlock *rwlock, BOOL *needSched)
364 {
365     UINT32 rwlockMode;
366     LosTaskCB *resumedTask = NULL;
367 
368     rwlock->rwCount = 0;
369     rwlock->writeOwner = NULL;
370     rwlockMode = OsRwlockGetMode(&(rwlock->readList), &(rwlock->writeList));
371     if (rwlockMode == RWLOCK_NONE_MODE) {
372         return LOS_OK;
373     }
374     /* In this case, rwlock will wake the first pended write task. */
375     if ((rwlockMode == RWLOCK_WRITE_MODE) || (rwlockMode == RWLOCK_WRITEFIRST_MODE)) {
376         resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->writeList)));
377         rwlock->rwCount = -1;
378         rwlock->writeOwner = (VOID *)resumedTask;
379         resumedTask->ops->wake(resumedTask);
380         if (needSched != NULL) {
381             *needSched = TRUE;
382         }
383         return LOS_OK;
384     }
385 
386     rwlock->rwCount = 1;
387     resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->readList)));
388     resumedTask->ops->wake(resumedTask);
389     while (!LOS_ListEmpty(&(rwlock->readList))) {
390         resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->readList)));
391         if (rwlockMode == RWLOCK_READFIRST_MODE) {
392             LosTaskCB *pendedWriteTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->writeList)));
393             if (OsSchedParamCompare(resumedTask, pendedWriteTask) >= 0) {
394                 break;
395             }
396         }
397         if (rwlock->rwCount == INT8_MAX) {
398             return EINVAL;
399         }
400         rwlock->rwCount++;
401         resumedTask->ops->wake(resumedTask);
402     }
403     if (needSched != NULL) {
404         *needSched = TRUE;
405     }
406     return LOS_OK;
407 }
408 
OsRwlockUnlockUnsafe(LosRwlock * rwlock,BOOL * needSched)409 UINT32 OsRwlockUnlockUnsafe(LosRwlock *rwlock, BOOL *needSched)
410 {
411     if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
412         return LOS_EBADF;
413     }
414 
415     if (rwlock->rwCount == 0) {
416         return LOS_EPERM;
417     }
418 
419     LosTaskCB *runTask = OsCurrTaskGet();
420     if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) != runTask)) {
421         return LOS_EPERM;
422     }
423 
424     /*
425      * When the rwCount of the rwlock more than 1 or less than -1, the rwlock mode will
426      * not changed after current unlock operation, so pended tasks can not be waken.
427      */
428     if (rwlock->rwCount > 1) {
429         rwlock->rwCount--;
430         return LOS_OK;
431     }
432 
433     if (rwlock->rwCount < -1) {
434         rwlock->rwCount++;
435         return LOS_OK;
436     }
437 
438     return OsRwlockPostOp(rwlock, needSched);
439 }
440 
LOS_RwlockUnLock(LosRwlock * rwlock)441 UINT32 LOS_RwlockUnLock(LosRwlock *rwlock)
442 {
443     UINT32 intSave;
444     BOOL needSched = FALSE;
445 
446     UINT32 ret = OsRwlockCheck(rwlock);
447     if (ret != LOS_OK) {
448         return ret;
449     }
450 
451     SCHEDULER_LOCK(intSave);
452     ret = OsRwlockUnlockUnsafe(rwlock, &needSched);
453     SCHEDULER_UNLOCK(intSave);
454     LOS_MpSchedule(OS_MP_CPU_ALL);
455     if (needSched == TRUE) {
456         LOS_Schedule();
457     }
458     return ret;
459 }
460 
461 #endif /* LOSCFG_BASE_IPC_RWLOCK */
462 
463