14d6c458bSopenharmony_ci/*
24d6c458bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
34d6c458bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44d6c458bSopenharmony_ci * you may not use this file except in compliance with the License.
54d6c458bSopenharmony_ci * You may obtain a copy of the License at
64d6c458bSopenharmony_ci *
74d6c458bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84d6c458bSopenharmony_ci *
94d6c458bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104d6c458bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114d6c458bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124d6c458bSopenharmony_ci * See the License for the specific language governing permissions and
134d6c458bSopenharmony_ci * limitations under the License.
144d6c458bSopenharmony_ci */
154d6c458bSopenharmony_ci
164d6c458bSopenharmony_ci#include <uv.h>
174d6c458bSopenharmony_ci#include "async_lock.h"
184d6c458bSopenharmony_ci#include "async_lock_manager.h"
194d6c458bSopenharmony_ci#include "helper/error_helper.h"
204d6c458bSopenharmony_ci#include "helper/napi_helper.h"
214d6c458bSopenharmony_ci#include "helper/object_helper.h"
224d6c458bSopenharmony_ci#include "tools/log.h"
234d6c458bSopenharmony_ci
244d6c458bSopenharmony_cinamespace Commonlibrary::Concurrent::LocksModule {
254d6c458bSopenharmony_ciusing namespace Commonlibrary::Concurrent::Common::Helper;
264d6c458bSopenharmony_ci
274d6c458bSopenharmony_ciAsyncLock::AsyncLock(const std::string &lockName)
284d6c458bSopenharmony_ci{
294d6c458bSopenharmony_ci    lockName_ = lockName;
304d6c458bSopenharmony_ci    anonymousLockId_ = 0;
314d6c458bSopenharmony_ci}
324d6c458bSopenharmony_ci
334d6c458bSopenharmony_ciAsyncLock::AsyncLock(uint32_t lockId)
344d6c458bSopenharmony_ci{
354d6c458bSopenharmony_ci    lockName_ = "";
364d6c458bSopenharmony_ci    anonymousLockId_ = lockId;
374d6c458bSopenharmony_ci}
384d6c458bSopenharmony_ci
394d6c458bSopenharmony_cinapi_value AsyncLock::LockAsync(napi_env env, napi_ref cb, LockMode mode, const LockOptions &options)
404d6c458bSopenharmony_ci{
414d6c458bSopenharmony_ci    napi_value promise;
424d6c458bSopenharmony_ci    napi_deferred deferred;
434d6c458bSopenharmony_ci    napi_create_promise(env, &deferred, &promise);
444d6c458bSopenharmony_ci    LockRequest *lockRequest =
454d6c458bSopenharmony_ci        new LockRequest(this, AsyncLockManager::GetCurrentTid(env), env, cb, mode, options, deferred);
464d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
474d6c458bSopenharmony_ci    if (!CanAcquireLock(lockRequest) && options.isAvailable) {
484d6c458bSopenharmony_ci        napi_value err;
494d6c458bSopenharmony_ci        NAPI_CALL(env, napi_create_string_utf8(env, "The lock is acquired", NAPI_AUTO_LENGTH, &err));
504d6c458bSopenharmony_ci        napi_reject_deferred(env, deferred, err);
514d6c458bSopenharmony_ci    } else {
524d6c458bSopenharmony_ci        lockRequest->OnQueued(options.timeoutMillis);
534d6c458bSopenharmony_ci        pendingList_.push_back(lockRequest);
544d6c458bSopenharmony_ci        ProcessPendingLockRequestUnsafe(env, lockRequest);
554d6c458bSopenharmony_ci    }
564d6c458bSopenharmony_ci    return promise;
574d6c458bSopenharmony_ci}
584d6c458bSopenharmony_ci
594d6c458bSopenharmony_civoid AsyncLock::CleanUpLockRequestOnCompletion(LockRequest* lockRequest)
604d6c458bSopenharmony_ci{
614d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
624d6c458bSopenharmony_ci    auto it = std::find(heldList_.begin(), heldList_.end(), lockRequest);
634d6c458bSopenharmony_ci    if (it == heldList_.end()) {
644d6c458bSopenharmony_ci        HILOG_FATAL("Lock is not found");
654d6c458bSopenharmony_ci        return;
664d6c458bSopenharmony_ci    }
674d6c458bSopenharmony_ci    heldList_.erase(it);
684d6c458bSopenharmony_ci    if (heldList_.empty()) {
694d6c458bSopenharmony_ci        // There are may be other shared lock requests in the heldList_.
704d6c458bSopenharmony_ci        // IF so, we mustn't change the status.
714d6c458bSopenharmony_ci        lockStatus_ = LOCK_MODE_UNLOCK;
724d6c458bSopenharmony_ci    }
734d6c458bSopenharmony_ci    napi_env env = lockRequest->GetEnv();
744d6c458bSopenharmony_ci    delete lockRequest;
754d6c458bSopenharmony_ci    ProcessPendingLockRequestUnsafe(env);
764d6c458bSopenharmony_ci}
774d6c458bSopenharmony_ci
784d6c458bSopenharmony_cibool AsyncLock::CleanUpLockRequestOnTimeout(LockRequest* lockRequest)
794d6c458bSopenharmony_ci{
804d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
814d6c458bSopenharmony_ci    auto it = std::find(pendingList_.begin(), pendingList_.end(), lockRequest);
824d6c458bSopenharmony_ci    if (it == pendingList_.end()) {
834d6c458bSopenharmony_ci        // the lock got held while we were waiting on the mutex, no-op
844d6c458bSopenharmony_ci        return false;
854d6c458bSopenharmony_ci    }
864d6c458bSopenharmony_ci    // we won the race, need to remove the request from the queue and handle the time out event
874d6c458bSopenharmony_ci    pendingList_.erase(it);
884d6c458bSopenharmony_ci    return true;
894d6c458bSopenharmony_ci}
904d6c458bSopenharmony_ci
914d6c458bSopenharmony_citemplate<bool isAsync>
924d6c458bSopenharmony_civoid AsyncLock::ProcessLockRequest(LockRequest* lockRequest)
934d6c458bSopenharmony_ci{
944d6c458bSopenharmony_ci    lockRequest->OnSatisfied();
954d6c458bSopenharmony_ci    heldList_.push_back(lockRequest);
964d6c458bSopenharmony_ci    pendingList_.pop_front();
974d6c458bSopenharmony_ci    asyncLockMutex_.unlock();
984d6c458bSopenharmony_ci    if constexpr (isAsync) {
994d6c458bSopenharmony_ci        lockRequest->CallCallbackAsync();
1004d6c458bSopenharmony_ci    } else {
1014d6c458bSopenharmony_ci        lockRequest->CallCallback();
1024d6c458bSopenharmony_ci    }
1034d6c458bSopenharmony_ci    asyncLockMutex_.lock();
1044d6c458bSopenharmony_ci}
1054d6c458bSopenharmony_ci
1064d6c458bSopenharmony_civoid AsyncLock::ProcessPendingLockRequest(napi_env env, LockRequest* syncLockRequest)
1074d6c458bSopenharmony_ci{
1084d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
1094d6c458bSopenharmony_ci    ProcessPendingLockRequestUnsafe(env, syncLockRequest);
1104d6c458bSopenharmony_ci}
1114d6c458bSopenharmony_ci
1124d6c458bSopenharmony_civoid AsyncLock::ProcessPendingLockRequestUnsafe(napi_env env, LockRequest* syncLockRequest)
1134d6c458bSopenharmony_ci{
1144d6c458bSopenharmony_ci    if (pendingList_.empty()) {
1154d6c458bSopenharmony_ci        if (refCount_ == 0) {
1164d6c458bSopenharmony_ci            // No more refs to the lock. We need to delete the instance but we cannot do it right now
1174d6c458bSopenharmony_ci            // because asyncLockMutex_ is acquired. Do it asynchronously.
1184d6c458bSopenharmony_ci            AsyncDestroy(env);
1194d6c458bSopenharmony_ci        }
1204d6c458bSopenharmony_ci        return;
1214d6c458bSopenharmony_ci    }
1224d6c458bSopenharmony_ci    LockRequest *lockRequest = pendingList_.front();
1234d6c458bSopenharmony_ci    if (!CanAcquireLock(lockRequest)) {
1244d6c458bSopenharmony_ci        return;
1254d6c458bSopenharmony_ci    }
1264d6c458bSopenharmony_ci    lockStatus_ = lockRequest->GetMode();
1274d6c458bSopenharmony_ci    if (lockStatus_ == LOCK_MODE_SHARED) {
1284d6c458bSopenharmony_ci        do {
1294d6c458bSopenharmony_ci            if (syncLockRequest == lockRequest) {
1304d6c458bSopenharmony_ci                ProcessLockRequest<false>(lockRequest);
1314d6c458bSopenharmony_ci            } else {
1324d6c458bSopenharmony_ci                ProcessLockRequest<true>(lockRequest);
1334d6c458bSopenharmony_ci            }
1344d6c458bSopenharmony_ci            if (pendingList_.empty()) {
1354d6c458bSopenharmony_ci                break;
1364d6c458bSopenharmony_ci            }
1374d6c458bSopenharmony_ci            lockRequest = pendingList_.front();
1384d6c458bSopenharmony_ci        } while (lockRequest->GetMode() == LOCK_MODE_SHARED);
1394d6c458bSopenharmony_ci    } else {
1404d6c458bSopenharmony_ci        ProcessLockRequest<true>(lockRequest);
1414d6c458bSopenharmony_ci    }
1424d6c458bSopenharmony_ci}
1434d6c458bSopenharmony_ci
1444d6c458bSopenharmony_cibool AsyncLock::CanAcquireLock(LockRequest *lockRequest)
1454d6c458bSopenharmony_ci{
1464d6c458bSopenharmony_ci    if (heldList_.empty()) {
1474d6c458bSopenharmony_ci        return true;
1484d6c458bSopenharmony_ci    }
1494d6c458bSopenharmony_ci    if (lockRequest->GetMode() == LOCK_MODE_SHARED && lockStatus_ == LOCK_MODE_SHARED) {
1504d6c458bSopenharmony_ci        return true;
1514d6c458bSopenharmony_ci    }
1524d6c458bSopenharmony_ci    if (lockStatus_ == LOCK_MODE_UNLOCK) {
1534d6c458bSopenharmony_ci        return true;
1544d6c458bSopenharmony_ci    }
1554d6c458bSopenharmony_ci    return false;
1564d6c458bSopenharmony_ci}
1574d6c458bSopenharmony_ci
1584d6c458bSopenharmony_cinapi_status AsyncLock::FillLockState(napi_env env, napi_value held, napi_value pending)
1594d6c458bSopenharmony_ci{
1604d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
1614d6c458bSopenharmony_ci    uint32_t idx = 0;
1624d6c458bSopenharmony_ci    for (LockRequest *rq : heldList_) {
1634d6c458bSopenharmony_ci        napi_value info = CreateLockInfo(env, rq);
1644d6c458bSopenharmony_ci        bool pendingException = false;
1654d6c458bSopenharmony_ci        napi_is_exception_pending(env, &pendingException);
1664d6c458bSopenharmony_ci        if (pendingException) {
1674d6c458bSopenharmony_ci            return napi_pending_exception;
1684d6c458bSopenharmony_ci        }
1694d6c458bSopenharmony_ci        napi_value index;
1704d6c458bSopenharmony_ci        napi_create_int32(env, idx, &index);
1714d6c458bSopenharmony_ci        napi_status status = napi_set_property(env, held, index, info);
1724d6c458bSopenharmony_ci        if (status != napi_ok) {
1734d6c458bSopenharmony_ci            return status;
1744d6c458bSopenharmony_ci        }
1754d6c458bSopenharmony_ci        ++idx;
1764d6c458bSopenharmony_ci    }
1774d6c458bSopenharmony_ci    idx = 0;
1784d6c458bSopenharmony_ci    for (LockRequest *rq : pendingList_) {
1794d6c458bSopenharmony_ci        napi_value info = CreateLockInfo(env, rq);
1804d6c458bSopenharmony_ci        bool pendingException = false;
1814d6c458bSopenharmony_ci        napi_is_exception_pending(env, &pendingException);
1824d6c458bSopenharmony_ci        if (pendingException) {
1834d6c458bSopenharmony_ci            return napi_pending_exception;
1844d6c458bSopenharmony_ci        }
1854d6c458bSopenharmony_ci        napi_value index;
1864d6c458bSopenharmony_ci        napi_create_int32(env, idx, &index);
1874d6c458bSopenharmony_ci        napi_status status = napi_set_property(env, pending, index, info);
1884d6c458bSopenharmony_ci        if (status != napi_ok) {
1894d6c458bSopenharmony_ci            return status;
1904d6c458bSopenharmony_ci        }
1914d6c458bSopenharmony_ci        ++idx;
1924d6c458bSopenharmony_ci    }
1934d6c458bSopenharmony_ci    return napi_ok;
1944d6c458bSopenharmony_ci}
1954d6c458bSopenharmony_ci
1964d6c458bSopenharmony_cinapi_value AsyncLock::CreateLockInfo(napi_env env, const LockRequest *rq)
1974d6c458bSopenharmony_ci{
1984d6c458bSopenharmony_ci    napi_value info;
1994d6c458bSopenharmony_ci    NAPI_CALL(env, napi_create_object(env, &info));
2004d6c458bSopenharmony_ci    napi_value name;
2014d6c458bSopenharmony_ci    NAPI_CALL(env, napi_create_string_utf8(env, lockName_.c_str(), NAPI_AUTO_LENGTH, &name));
2024d6c458bSopenharmony_ci    napi_value mode;
2034d6c458bSopenharmony_ci    NAPI_CALL(env, napi_create_int32(env, rq->GetMode(), &mode));
2044d6c458bSopenharmony_ci    napi_value tid;
2054d6c458bSopenharmony_ci    NAPI_CALL(env, napi_create_int32(env, rq->GetTid(), &tid));
2064d6c458bSopenharmony_ci
2074d6c458bSopenharmony_ci    napi_property_descriptor properties[] = {
2084d6c458bSopenharmony_ci        DECLARE_NAPI_PROPERTY("name", name),
2094d6c458bSopenharmony_ci        DECLARE_NAPI_PROPERTY("mode", mode),
2104d6c458bSopenharmony_ci        DECLARE_NAPI_PROPERTY("contextId", tid),
2114d6c458bSopenharmony_ci    };
2124d6c458bSopenharmony_ci    NAPI_CALL(env, napi_define_properties(env, info, sizeof(properties) / sizeof(properties[0]), properties));
2134d6c458bSopenharmony_ci    return info;
2144d6c458bSopenharmony_ci}
2154d6c458bSopenharmony_ci
2164d6c458bSopenharmony_civoid AsyncLock::AsyncDestroy(napi_env env)
2174d6c458bSopenharmony_ci{
2184d6c458bSopenharmony_ci    napi_value resourceName;
2194d6c458bSopenharmony_ci    napi_create_string_utf8(env, "AsyncLock::AsyncDestroyCallback", NAPI_AUTO_LENGTH, &resourceName);
2204d6c458bSopenharmony_ci    auto *data = new std::pair<AsyncLock *, napi_async_work>();
2214d6c458bSopenharmony_ci    data->first = this;
2224d6c458bSopenharmony_ci    napi_async_work &work = data->second;
2234d6c458bSopenharmony_ci    napi_status status = napi_create_async_work(
2244d6c458bSopenharmony_ci        env, nullptr, resourceName, [](napi_env, void *) {}, AsyncDestroyCallback, data, &work);
2254d6c458bSopenharmony_ci    if (status != napi_ok) {
2264d6c458bSopenharmony_ci        HILOG_FATAL("Internal error: cannot create async work");
2274d6c458bSopenharmony_ci    }
2284d6c458bSopenharmony_ci    status = napi_queue_async_work(env, work);
2294d6c458bSopenharmony_ci    if (status != napi_ok) {
2304d6c458bSopenharmony_ci        HILOG_FATAL("Internal error: cannot queue async work");
2314d6c458bSopenharmony_ci    }
2324d6c458bSopenharmony_ci}
2334d6c458bSopenharmony_ci
2344d6c458bSopenharmony_civoid AsyncLock::AsyncDestroyCallback(napi_env env, napi_status, void *data)
2354d6c458bSopenharmony_ci{
2364d6c458bSopenharmony_ci    auto *lockAndWork = reinterpret_cast<std::pair<AsyncLock *, napi_async_work> *>(data);
2374d6c458bSopenharmony_ci    delete lockAndWork->first;
2384d6c458bSopenharmony_ci    napi_delete_async_work(env, lockAndWork->second);
2394d6c458bSopenharmony_ci    delete lockAndWork;
2404d6c458bSopenharmony_ci}
2414d6c458bSopenharmony_ci
2424d6c458bSopenharmony_ciuint32_t AsyncLock::IncRefCount()
2434d6c458bSopenharmony_ci{
2444d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
2454d6c458bSopenharmony_ci    return ++refCount_;
2464d6c458bSopenharmony_ci}
2474d6c458bSopenharmony_ci
2484d6c458bSopenharmony_ciuint32_t AsyncLock::DecRefCount()
2494d6c458bSopenharmony_ci{
2504d6c458bSopenharmony_ci    asyncLockMutex_.lock();
2514d6c458bSopenharmony_ci    uint32_t count = --refCount_;
2524d6c458bSopenharmony_ci    if (count == 0) {
2534d6c458bSopenharmony_ci        // No refs to the instance. We can delete it right now if there are no more lock requests.
2544d6c458bSopenharmony_ci        // In case there are some lock requests, the last processed lock request will delete the instance.
2554d6c458bSopenharmony_ci        if (pendingList_.size() == 0 && heldList_.size() == 0) {
2564d6c458bSopenharmony_ci            asyncLockMutex_.unlock();
2574d6c458bSopenharmony_ci            delete this;
2584d6c458bSopenharmony_ci            return 0;
2594d6c458bSopenharmony_ci        }
2604d6c458bSopenharmony_ci    }
2614d6c458bSopenharmony_ci    asyncLockMutex_.unlock();
2624d6c458bSopenharmony_ci    return count;
2634d6c458bSopenharmony_ci}
2644d6c458bSopenharmony_ci
2654d6c458bSopenharmony_cistd::vector<RequestCreationInfo> AsyncLock::GetSatisfiedRequestInfos()
2664d6c458bSopenharmony_ci{
2674d6c458bSopenharmony_ci    std::vector<RequestCreationInfo> result;
2684d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
2694d6c458bSopenharmony_ci    for (auto *request : heldList_) {
2704d6c458bSopenharmony_ci        result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
2714d6c458bSopenharmony_ci    }
2724d6c458bSopenharmony_ci    return result;
2734d6c458bSopenharmony_ci}
2744d6c458bSopenharmony_ci
2754d6c458bSopenharmony_cistd::vector<RequestCreationInfo> AsyncLock::GetPendingRequestInfos()
2764d6c458bSopenharmony_ci{
2774d6c458bSopenharmony_ci    std::vector<RequestCreationInfo> result;
2784d6c458bSopenharmony_ci    std::unique_lock<std::mutex> lock(asyncLockMutex_);
2794d6c458bSopenharmony_ci    for (auto *request : pendingList_) {
2804d6c458bSopenharmony_ci        result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
2814d6c458bSopenharmony_ci    }
2824d6c458bSopenharmony_ci    return result;
2834d6c458bSopenharmony_ci}
2844d6c458bSopenharmony_ci
2854d6c458bSopenharmony_ci} // namespace Commonlibrary::Concurrent::LocksModule
286