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