* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "shared_mutex_private.h"
#include "dfx/log/ffrt_log_api.h"
#include "dfx/trace/ffrt_trace.h"
#include "internal_inc/osal.h"
#include "internal_inc/types.h"
#include "tm/cpu_task.h"
namespace ffrt {
void SharedMutexPrivate::Lock()
{
std::lock_guard lg(mut);
while (state & writeEntered) {
Wait(wList1, SharedMutexWaitType::WRITE);
}
state |= writeEntered;
while (state & readersMax) {
Wait(wList2, SharedMutexWaitType::NORMAL);
}
}
bool SharedMutexPrivate::TryLock()
{
std::lock_guard lg(mut);
if (state == 0) {
state = writeEntered;
return true;
}
return false;
}
void SharedMutexPrivate::LockShared()
{
std::lock_guard lg(mut);
while (state >= readersMax) {
Wait(wList1, SharedMutexWaitType::READ);
}
++state;
}
bool SharedMutexPrivate::TryLockShared()
{
std::lock_guard lg(mut);
if (state < readersMax) {
++state;
return true;
}
return false;
}
void SharedMutexPrivate::Unlock()
{
std::lock_guard lg(mut);
if (state == writeEntered) {
state = 0;
NotifyAll(wList1);
return;
}
if (state > 0) {
--state;
if (state & writeEntered) {
if (state == writeEntered) {
NotifyOne(wList2);
}
} else {
if (state == readersMax - 1) {
NotifyOne(wList1);
} else if (!wList1.Empty()) {
NotifyAll(wList1);
}
}
}
}
void SharedMutexPrivate::Wait(LinkedList& wList, SharedMutexWaitType wtType)
{
auto ctx = ExecuteCtx::Cur();
auto task = ctx->task;
if (task == nullptr || task->Block() == BlockType::BLOCK_THREAD) {
ctx->wn.task = task;
ctx->wn.wtType = wtType;
wList.PushBack(ctx->wn.node);
std::unique_lock<std::mutex> lk(ctx->wn.wl);
mut.unlock();
ctx->wn.cv.wait(lk);
ctx->wn.task = nullptr;
if (task) {
task->Wake();
}
} else {
FFRT_BLOCK_TRACER(task->gid, smx);
CoWait([&](CoTask* task) -> bool {
task->we.wtType = wtType;
wList.PushBack(task->we.node);
mut.unlock();
return true;
});
}
mut.lock();
}
void SharedMutexPrivate::NotifyOne(LinkedList& wList)
{
WaitEntry* we = wList.PopFront(&WaitEntry::node);
if (we != nullptr) {
auto task = we->task;
if (task == nullptr || task->GetBlockType() == BlockType::BLOCK_THREAD) {
WaitUntilEntry* wue = static_cast<WaitUntilEntry*>(we);
std::lock_guard<std::mutex> lk(wue->wl);
wue->cv.notify_one();
} else {
CoWake(static_cast<CoTask*>(task), CoWakeType::NO_TIMEOUT_WAKE);
}
}
}
void SharedMutexPrivate::NotifyAll(LinkedList& wList)
{
WaitEntry* we = wList.PopFront(&WaitEntry::node);
while (we != nullptr) {
auto task = we->task;
if (task == nullptr || task->GetBlockType() == BlockType::BLOCK_THREAD) {
WaitUntilEntry* wue = static_cast<WaitUntilEntry*>(we);
std::lock_guard<std::mutex> lk(wue->wl);
wue->cv.notify_one();
} else {
CoWake(static_cast<CoTask*>(task), CoWakeType::NO_TIMEOUT_WAKE);
}
if (we->wtType == SharedMutexWaitType::READ) {
WaitEntry* weNext = wList.Front(&WaitEntry::node);
if (weNext != nullptr && weNext->wtType == SharedMutexWaitType::WRITE) {
return;
}
} else if (we->wtType == SharedMutexWaitType::WRITE) {
return;
}
we = wList.PopFront(&WaitEntry::node);
}
}
}
#ifdef __cplusplus
extern "C" {
#endif
API_ATTRIBUTE((visibility("default")))
int ffrt_rwlock_init(ffrt_rwlock_t* rwlock, const ffrt_rwlockattr_t* attr)
{
if (!rwlock) {
FFRT_LOGE("rwlock should not be empty");
return ffrt_error_inval;
}
if (attr != nullptr) {
FFRT_LOGE("only support normal rwlock");
return ffrt_error_inval;
}
static_assert(sizeof(ffrt::SharedMutexPrivate) <= ffrt_rwlock_storage_size,
"size must be less than ffrt_rwlock_storage_size");
new (rwlock)ffrt::SharedMutexPrivate();
return ffrt_success;
}
API_ATTRIBUTE((visibility("default")))
int ffrt_rwlock_wrlock(ffrt_rwlock_t* rwlock)
{
if (!rwlock) {
FFRT_LOGE("rwlock should not be empty");
return ffrt_error_inval;
}
auto p = reinterpret_cast<ffrt::SharedMutexPrivate*>(rwlock);
p->Lock();
return ffrt_success;
}
API_ATTRIBUTE((visibility("default")))
int ffrt_rwlock_trywrlock(ffrt_rwlock_t* rwlock)
{
if (!rwlock) {
FFRT_LOGE("rwlock should not be empty");
return ffrt_error_inval;
}
auto p = reinterpret_cast<ffrt::SharedMutexPrivate*>(rwlock);
return p->TryLock() ? ffrt_success : ffrt_error_busy;
}
API_ATTRIBUTE((visibility("default")))
int ffrt_rwlock_rdlock(ffrt_rwlock_t* rwlock)
{
if (!rwlock) {
FFRT_LOGE("rwlock should not be empty");
return ffrt_error_inval;
}
auto p = reinterpret_cast<ffrt::SharedMutexPrivate*>(rwlock);
p->LockShared();
return ffrt_success;
}
API_ATTRIBUTE((visibility("default")))
int ffrt_rwlock_tryrdlock(ffrt_rwlock_t* rwlock)
{
if (!rwlock) {
FFRT_LOGE("rwlock should not be empty");
return ffrt_error_inval;
}
auto p = reinterpret_cast<ffrt::SharedMutexPrivate*>(rwlock);
return p->TryLockShared() ? ffrt_success : ffrt_error_busy;
}
API_ATTRIBUTE((visibility("default")))
int ffrt_rwlock_unlock(ffrt_rwlock_t* rwlock)
{
if (!rwlock) {
FFRT_LOGE("rwlock should not be empty");
return ffrt_error_inval;
}
auto p = reinterpret_cast<ffrt::SharedMutexPrivate*>(rwlock);
p->Unlock();
return ffrt_success;
}
API_ATTRIBUTE((visibility("default")))
int ffrt_rwlock_destroy(ffrt_rwlock_t* rwlock)
{
if (!rwlock) {
FFRT_LOGE("rwlock should not be empty");
return ffrt_error_inval;
}
auto p = reinterpret_cast<ffrt::SharedMutexPrivate*>(rwlock);
p->~SharedMutexPrivate();
return ffrt_success;
}
#ifdef __cplusplus
}
#endif