kernel_liteos_a/kernel/base/ipc/los_rwlock.c

465 lines
13 KiB
C

/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_rwlock_pri.h"
#include "stdint.h"
#include "los_spinlock.h"
#include "los_mp.h"
#include "los_task_pri.h"
#include "los_exc.h"
#include "los_sched_pri.h"
#ifdef LOSCFG_BASE_IPC_RWLOCK
#define RWLOCK_COUNT_MASK 0x00FFFFFFU
BOOL LOS_RwlockIsValid(const LosRwlock *rwlock)
{
if ((rwlock != NULL) && ((rwlock->magic & RWLOCK_COUNT_MASK) == OS_RWLOCK_MAGIC)) {
return TRUE;
}
return FALSE;
}
UINT32 LOS_RwlockInit(LosRwlock *rwlock)
{
UINT32 intSave;
if (rwlock == NULL) {
return LOS_EINVAL;
}
SCHEDULER_LOCK(intSave);
if ((rwlock->magic & RWLOCK_COUNT_MASK) == OS_RWLOCK_MAGIC) {
SCHEDULER_UNLOCK(intSave);
return LOS_EPERM;
}
rwlock->rwCount = 0;
rwlock->writeOwner = NULL;
LOS_ListInit(&(rwlock->readList));
LOS_ListInit(&(rwlock->writeList));
rwlock->magic = OS_RWLOCK_MAGIC;
SCHEDULER_UNLOCK(intSave);
return LOS_OK;
}
UINT32 LOS_RwlockDestroy(LosRwlock *rwlock)
{
UINT32 intSave;
if (rwlock == NULL) {
return LOS_EINVAL;
}
SCHEDULER_LOCK(intSave);
if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
SCHEDULER_UNLOCK(intSave);
return LOS_EBADF;
}
if (rwlock->rwCount != 0) {
SCHEDULER_UNLOCK(intSave);
return LOS_EBUSY;
}
(VOID)memset_s(rwlock, sizeof(LosRwlock), 0, sizeof(LosRwlock));
SCHEDULER_UNLOCK(intSave);
return LOS_OK;
}
STATIC UINT32 OsRwlockCheck(LosRwlock *rwlock)
{
if (rwlock == NULL) {
return LOS_EINVAL;
}
if (OS_INT_ACTIVE) {
return LOS_EINTR;
}
/* DO NOT Call blocking API in system tasks */
LosTaskCB *runTask = (LosTaskCB *)OsCurrTaskGet();
if (runTask->taskStatus & OS_TASK_FLAG_SYSTEM_TASK) {
return LOS_EPERM;
}
return LOS_OK;
}
STATIC BOOL OsRwlockPriCompare(LosTaskCB *runTask, LOS_DL_LIST *rwList)
{
if (!LOS_ListEmpty(rwList)) {
LosTaskCB *highestTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(rwList));
if (runTask->priority < highestTask->priority) {
return TRUE;
}
return FALSE;
}
return TRUE;
}
STATIC UINT32 OsRwlockRdPendOp(LosTaskCB *runTask, LosRwlock *rwlock, UINT32 timeout)
{
UINT32 ret;
/*
* When the rwlock mode is read mode or free mode and the priority of the current read task
* is higher than the first pended write task. current read task can obtain this rwlock.
*/
if (rwlock->rwCount >= 0) {
if (OsRwlockPriCompare(runTask, &(rwlock->writeList))) {
if (rwlock->rwCount == INT8_MAX) {
return LOS_EINVAL;
}
rwlock->rwCount++;
return LOS_OK;
}
}
if (!timeout) {
return LOS_EINVAL;
}
if (!OsPreemptableInSched()) {
return LOS_EDEADLK;
}
/* The current task is not allowed to obtain the write lock when it obtains the read lock. */
if ((LosTaskCB *)(rwlock->writeOwner) == runTask) {
return LOS_EINVAL;
}
/*
* When the rwlock mode is write mode or the priority of the current read task
* is lower than the first pended write task, current read task will be pended.
*/
LOS_DL_LIST *node = OsSchedLockPendFindPos(runTask, &(rwlock->readList));
ret = OsSchedTaskWait(node, timeout, TRUE);
if (ret == LOS_ERRNO_TSK_TIMEOUT) {
return LOS_ETIMEDOUT;
}
return ret;
}
STATIC UINT32 OsRwlockWrPendOp(LosTaskCB *runTask, LosRwlock *rwlock, UINT32 timeout)
{
UINT32 ret;
/* When the rwlock is free mode, current write task can obtain this rwlock. */
if (rwlock->rwCount == 0) {
rwlock->rwCount = -1;
rwlock->writeOwner = (VOID *)runTask;
return LOS_OK;
}
/* Current write task can use one rwlock once again if the rwlock owner is it. */
if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) == runTask)) {
if (rwlock->rwCount == INT8_MIN) {
return LOS_EINVAL;
}
rwlock->rwCount--;
return LOS_OK;
}
if (!timeout) {
return LOS_EINVAL;
}
if (!OsPreemptableInSched()) {
return LOS_EDEADLK;
}
/*
* When the rwlock is read mode or other write task obtains this rwlock, current
* write task will be pended.
*/
LOS_DL_LIST *node = OsSchedLockPendFindPos(runTask, &(rwlock->writeList));
ret = OsSchedTaskWait(node, timeout, TRUE);
if (ret == LOS_ERRNO_TSK_TIMEOUT) {
ret = LOS_ETIMEDOUT;
}
return ret;
}
UINT32 OsRwlockRdUnsafe(LosRwlock *rwlock, UINT32 timeout)
{
if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
return LOS_EBADF;
}
return OsRwlockRdPendOp(OsCurrTaskGet(), rwlock, timeout);
}
UINT32 OsRwlockTryRdUnsafe(LosRwlock *rwlock, UINT32 timeout)
{
if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
return LOS_EBADF;
}
LosTaskCB *runTask = OsCurrTaskGet();
if ((LosTaskCB *)(rwlock->writeOwner) == runTask) {
return LOS_EINVAL;
}
/*
* When the rwlock mode is read mode or free mode and the priority of the current read task
* is lower than the first pended write task, current read task can not obtain the rwlock.
*/
if ((rwlock->rwCount >= 0) && !OsRwlockPriCompare(runTask, &(rwlock->writeList))) {
return LOS_EBUSY;
}
/*
* When the rwlock mode is write mode, current read task can not obtain the rwlock.
*/
if (rwlock->rwCount < 0) {
return LOS_EBUSY;
}
return OsRwlockRdPendOp(runTask, rwlock, timeout);
}
UINT32 OsRwlockWrUnsafe(LosRwlock *rwlock, UINT32 timeout)
{
if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
return LOS_EBADF;
}
return OsRwlockWrPendOp(OsCurrTaskGet(), rwlock, timeout);
}
UINT32 OsRwlockTryWrUnsafe(LosRwlock *rwlock, UINT32 timeout)
{
if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
return LOS_EBADF;
}
/* When the rwlock is read mode, current write task will be pended. */
if (rwlock->rwCount > 0) {
return LOS_EBUSY;
}
/* When other write task obtains this rwlock, current write task will be pended. */
LosTaskCB *runTask = OsCurrTaskGet();
if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) != runTask)) {
return LOS_EBUSY;
}
return OsRwlockWrPendOp(runTask, rwlock, timeout);
}
UINT32 LOS_RwlockRdLock(LosRwlock *rwlock, UINT32 timeout)
{
UINT32 intSave;
UINT32 ret = OsRwlockCheck(rwlock);
if (ret != LOS_OK) {
return ret;
}
SCHEDULER_LOCK(intSave);
ret = OsRwlockRdUnsafe(rwlock, timeout);
SCHEDULER_UNLOCK(intSave);
return ret;
}
UINT32 LOS_RwlockTryRdLock(LosRwlock *rwlock)
{
UINT32 intSave;
UINT32 ret = OsRwlockCheck(rwlock);
if (ret != LOS_OK) {
return ret;
}
SCHEDULER_LOCK(intSave);
ret = OsRwlockTryRdUnsafe(rwlock, 0);
SCHEDULER_UNLOCK(intSave);
return ret;
}
UINT32 LOS_RwlockWrLock(LosRwlock *rwlock, UINT32 timeout)
{
UINT32 intSave;
UINT32 ret = OsRwlockCheck(rwlock);
if (ret != LOS_OK) {
return ret;
}
SCHEDULER_LOCK(intSave);
ret = OsRwlockWrUnsafe(rwlock, timeout);
SCHEDULER_UNLOCK(intSave);
return ret;
}
UINT32 LOS_RwlockTryWrLock(LosRwlock *rwlock)
{
UINT32 intSave;
UINT32 ret = OsRwlockCheck(rwlock);
if (ret != LOS_OK) {
return ret;
}
SCHEDULER_LOCK(intSave);
ret = OsRwlockTryWrUnsafe(rwlock, 0);
SCHEDULER_UNLOCK(intSave);
return ret;
}
STATIC UINT32 OsRwlockGetMode(LOS_DL_LIST *readList, LOS_DL_LIST *writeList)
{
BOOL isReadEmpty = LOS_ListEmpty(readList);
BOOL isWriteEmpty = LOS_ListEmpty(writeList);
if (isReadEmpty && isWriteEmpty) {
return RWLOCK_NONE_MODE;
}
if (!isReadEmpty && isWriteEmpty) {
return RWLOCK_READ_MODE;
}
if (isReadEmpty && !isWriteEmpty) {
return RWLOCK_WRITE_MODE;
}
LosTaskCB *pendedReadTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(readList));
LosTaskCB *pendedWriteTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(writeList));
if (pendedWriteTask->priority <= pendedReadTask->priority) {
return RWLOCK_WRITEFIRST_MODE;
}
return RWLOCK_READFIRST_MODE;
}
STATIC UINT32 OsRwlockPostOp(LosRwlock *rwlock, BOOL *needSched)
{
UINT32 rwlockMode;
LosTaskCB *resumedTask = NULL;
UINT16 pendedWriteTaskPri;
rwlock->rwCount = 0;
rwlock->writeOwner = NULL;
rwlockMode = OsRwlockGetMode(&(rwlock->readList), &(rwlock->writeList));
if (rwlockMode == RWLOCK_NONE_MODE) {
return LOS_OK;
}
/* In this case, rwlock will wake the first pended write task. */
if ((rwlockMode == RWLOCK_WRITE_MODE) || (rwlockMode == RWLOCK_WRITEFIRST_MODE)) {
resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->writeList)));
rwlock->rwCount = -1;
rwlock->writeOwner = (VOID *)resumedTask;
OsSchedTaskWake(resumedTask);
if (needSched != NULL) {
*needSched = TRUE;
}
return LOS_OK;
}
/* In this case, rwlock will wake the valid pended read task. */
if (rwlockMode == RWLOCK_READFIRST_MODE) {
pendedWriteTaskPri = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->writeList)))->priority;
}
resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->readList)));
rwlock->rwCount = 1;
OsSchedTaskWake(resumedTask);
while (!LOS_ListEmpty(&(rwlock->readList))) {
resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->readList)));
if ((rwlockMode == RWLOCK_READFIRST_MODE) && (resumedTask->priority >= pendedWriteTaskPri)) {
break;
}
if (rwlock->rwCount == INT8_MAX) {
return EINVAL;
}
rwlock->rwCount++;
OsSchedTaskWake(resumedTask);
}
if (needSched != NULL) {
*needSched = TRUE;
}
return LOS_OK;
}
UINT32 OsRwlockUnlockUnsafe(LosRwlock *rwlock, BOOL *needSched)
{
if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
return LOS_EBADF;
}
if (rwlock->rwCount == 0) {
return LOS_EPERM;
}
LosTaskCB *runTask = OsCurrTaskGet();
if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) != runTask)) {
return LOS_EPERM;
}
/*
* When the rwCount of the rwlock more than 1 or less than -1, the rwlock mode will
* not changed after current unlock operation, so pended tasks can not be waken.
*/
if (rwlock->rwCount > 1) {
rwlock->rwCount--;
return LOS_OK;
}
if (rwlock->rwCount < -1) {
rwlock->rwCount++;
return LOS_OK;
}
return OsRwlockPostOp(rwlock, needSched);
}
UINT32 LOS_RwlockUnLock(LosRwlock *rwlock)
{
UINT32 intSave;
BOOL needSched = FALSE;
UINT32 ret = OsRwlockCheck(rwlock);
if (ret != LOS_OK) {
return ret;
}
SCHEDULER_LOCK(intSave);
ret = OsRwlockUnlockUnsafe(rwlock, &needSched);
SCHEDULER_UNLOCK(intSave);
LOS_MpSchedule(OS_MP_CPU_ALL);
if (needSched == TRUE) {
LOS_Schedule();
}
return ret;
}
#endif /* LOSCFG_BASE_IPC_RWLOCK */