• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "async_lock.h"
17 #include "async_lock_manager.h"
18 #include "tools/log.h"
19 
20 namespace Commonlibrary::Concurrent::LocksModule {
21 using namespace Commonlibrary::Concurrent::Common::Helper;
22 
AsyncLock(const std::string & lockName)23 AsyncLock::AsyncLock(const std::string &lockName)
24 {
25     lockName_ = lockName;
26     anonymousLockId_ = 0;
27 }
28 
AsyncLock(uint32_t lockId)29 AsyncLock::AsyncLock(uint32_t lockId)
30 {
31     lockName_ = "";
32     anonymousLockId_ = lockId;
33 }
34 
LockAsync(napi_env env,napi_ref cb,LockMode mode,const LockOptions & options)35 napi_value AsyncLock::LockAsync(napi_env env, napi_ref cb, LockMode mode, const LockOptions &options)
36 {
37     napi_value promise;
38     napi_deferred deferred;
39     napi_create_promise(env, &deferred, &promise);
40     LockRequest *lockRequest =
41         new LockRequest(this, AsyncLockManager::GetCurrentTid(env), env, cb, mode, options, deferred);
42     std::unique_lock<std::mutex> lock(asyncLockMutex_);
43     if (!CanAcquireLock(lockRequest) && options.isAvailable) {
44         napi_value err;
45         NAPI_CALL(env, napi_create_string_utf8(env, "The lock is acquired", NAPI_AUTO_LENGTH, &err));
46         napi_reject_deferred(env, deferred, err);
47     } else {
48         lockRequest->OnQueued(env, options.timeoutMillis);
49         pendingList_.push_back(lockRequest);
50         ProcessPendingLockRequestUnsafe(env, lockRequest);
51     }
52     return promise;
53 }
54 
CleanUpLockRequestOnCompletion(LockRequest * lockRequest)55 void AsyncLock::CleanUpLockRequestOnCompletion(LockRequest* lockRequest)
56 {
57     std::unique_lock<std::mutex> lock(asyncLockMutex_);
58     auto it = std::find(heldList_.begin(), heldList_.end(), lockRequest);
59     if (it == heldList_.end()) {
60         HILOG_FATAL("Lock is not found");
61         return;
62     }
63     heldList_.erase(it);
64     if (heldList_.empty()) {
65         // There are may be other shared lock requests in the heldList_.
66         // IF so, we mustn't change the status.
67         lockStatus_ = LOCK_MODE_UNLOCK;
68     }
69     napi_env env = lockRequest->GetEnv();
70     delete lockRequest;
71     if (pendingList_.empty()) {
72         if (refCount_ == 0 && heldList_.empty()) {
73             lock.unlock();
74             AsyncLockManager::CheckAndRemoveLock(this);
75         }
76         return;
77     }
78     ProcessPendingLockRequestUnsafe(env);
79 }
80 
CleanUpLockRequestOnTimeout(LockRequest * lockRequest)81 bool AsyncLock::CleanUpLockRequestOnTimeout(LockRequest* lockRequest)
82 {
83     std::unique_lock<std::mutex> lock(asyncLockMutex_);
84     auto it = std::find(pendingList_.begin(), pendingList_.end(), lockRequest);
85     if (it == pendingList_.end()) {
86         // the lock got held while we were waiting on the mutex, no-op
87         return false;
88     }
89     // we won the race, need to remove the request from the queue and handle the time out event
90     pendingList_.erase(it);
91     return true;
92 }
93 
94 template <bool isAsync>
ProcessLockRequest(napi_env env,LockRequest * lockRequest)95 void AsyncLock::ProcessLockRequest(napi_env env, LockRequest *lockRequest)
96 {
97     lockRequest->OnSatisfied(env);
98     heldList_.push_back(lockRequest);
99     pendingList_.pop_front();
100     asyncLockMutex_.unlock();
101     if constexpr (isAsync) {
102         lockRequest->CallCallbackAsync();
103     } else {
104         lockRequest->CallCallback();
105     }
106     asyncLockMutex_.lock();
107 }
108 
ProcessPendingLockRequest(napi_env env,LockRequest * syncLockRequest)109 void AsyncLock::ProcessPendingLockRequest(napi_env env, LockRequest* syncLockRequest)
110 {
111     std::unique_lock<std::mutex> lock(asyncLockMutex_);
112     if (pendingList_.empty()) {
113         if (refCount_ == 0 && heldList_.empty()) {
114             lock.unlock();
115             AsyncLockManager::CheckAndRemoveLock(this);
116         }
117         return;
118     }
119     ProcessPendingLockRequestUnsafe(env, syncLockRequest);
120 }
121 
ProcessPendingLockRequestUnsafe(napi_env env,LockRequest * syncLockRequest)122 void AsyncLock::ProcessPendingLockRequestUnsafe(napi_env env, LockRequest *syncLockRequest)
123 {
124     LockRequest *lockRequest = pendingList_.front();
125     if (!CanAcquireLock(lockRequest)) {
126         return;
127     }
128     lockStatus_ = lockRequest->GetMode();
129     if (lockStatus_ == LOCK_MODE_SHARED) {
130         do {
131             if (syncLockRequest == lockRequest) {
132                 ProcessLockRequest<false>(env, lockRequest);
133             } else {
134                 ProcessLockRequest<true>(env, lockRequest);
135             }
136             if (pendingList_.empty()) {
137                 break;
138             }
139             lockRequest = pendingList_.front();
140         } while (lockRequest->GetMode() == LOCK_MODE_SHARED);
141     } else {
142         ProcessLockRequest<true>(env, lockRequest);
143     }
144 }
145 
CanAcquireLock(LockRequest * lockRequest)146 bool AsyncLock::CanAcquireLock(LockRequest *lockRequest)
147 {
148     if (heldList_.empty()) {
149         return true;
150     }
151     if (lockRequest->GetMode() == LOCK_MODE_SHARED && lockStatus_ == LOCK_MODE_SHARED) {
152         return true;
153     }
154     if (lockStatus_ == LOCK_MODE_UNLOCK) {
155         return true;
156     }
157     return false;
158 }
159 
FillLockState(napi_env env,napi_value held,napi_value pending)160 napi_status AsyncLock::FillLockState(napi_env env, napi_value held, napi_value pending)
161 {
162     std::unique_lock<std::mutex> lock(asyncLockMutex_);
163     uint32_t idx = 0;
164     for (LockRequest *rq : heldList_) {
165         napi_value info = CreateLockInfo(env, rq);
166         bool pendingException = false;
167         napi_is_exception_pending(env, &pendingException);
168         if (pendingException) {
169             return napi_pending_exception;
170         }
171         napi_value index;
172         napi_create_int32(env, idx, &index);
173         napi_status status = napi_set_property(env, held, index, info);
174         if (status != napi_ok) {
175             return status;
176         }
177         ++idx;
178     }
179     idx = 0;
180     for (LockRequest *rq : pendingList_) {
181         napi_value info = CreateLockInfo(env, rq);
182         bool pendingException = false;
183         napi_is_exception_pending(env, &pendingException);
184         if (pendingException) {
185             return napi_pending_exception;
186         }
187         napi_value index;
188         napi_create_int32(env, idx, &index);
189         napi_status status = napi_set_property(env, pending, index, info);
190         if (status != napi_ok) {
191             return status;
192         }
193         ++idx;
194     }
195     return napi_ok;
196 }
197 
CreateLockInfo(napi_env env,const LockRequest * rq)198 napi_value AsyncLock::CreateLockInfo(napi_env env, const LockRequest *rq)
199 {
200     napi_value info;
201     NAPI_CALL(env, napi_create_object(env, &info));
202     napi_value name;
203     NAPI_CALL(env, napi_create_string_utf8(env, lockName_.c_str(), NAPI_AUTO_LENGTH, &name));
204     napi_value mode;
205     NAPI_CALL(env, napi_create_int32(env, rq->GetMode(), &mode));
206     napi_value tid;
207     NAPI_CALL(env, napi_create_int32(env, rq->GetTid(), &tid));
208 
209     napi_property_descriptor properties[] = {
210         DECLARE_NAPI_PROPERTY("name", name),
211         DECLARE_NAPI_PROPERTY("mode", mode),
212         DECLARE_NAPI_PROPERTY("contextId", tid),
213     };
214     NAPI_CALL(env, napi_define_properties(env, info, sizeof(properties) / sizeof(properties[0]), properties));
215     return info;
216 }
217 
IsReadyForDeletion()218 bool AsyncLock::IsReadyForDeletion()
219 {
220     std::unique_lock<std::mutex> lock(asyncLockMutex_);
221     return refCount_ == 0 && pendingList_.empty() && heldList_.empty();
222 }
223 
IncRefCount()224 uint32_t AsyncLock::IncRefCount()
225 {
226     std::unique_lock<std::mutex> lock(asyncLockMutex_);
227     return ++refCount_;
228 }
229 
DecRefCount()230 uint32_t AsyncLock::DecRefCount()
231 {
232     std::unique_lock<std::mutex> lock(asyncLockMutex_);
233     return --refCount_;
234 }
235 
GetSatisfiedRequestInfos()236 std::vector<RequestCreationInfo> AsyncLock::GetSatisfiedRequestInfos()
237 {
238     std::vector<RequestCreationInfo> result;
239     std::unique_lock<std::mutex> lock(asyncLockMutex_);
240     for (auto *request : heldList_) {
241         result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
242     }
243     return result;
244 }
245 
GetPendingRequestInfos()246 std::vector<RequestCreationInfo> AsyncLock::GetPendingRequestInfos()
247 {
248     std::vector<RequestCreationInfo> result;
249     std::unique_lock<std::mutex> lock(asyncLockMutex_);
250     for (auto *request : pendingList_) {
251         result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
252     }
253     return result;
254 }
255 
256 } // namespace Commonlibrary::Concurrent::LocksModule
257