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 if (!NativeEngine::IsAlive(reinterpret_cast<NativeEngine *>(env))) {
41 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "env is not alive");
42 return nullptr;
43 }
44 if (!CanAcquireLock(mode) && options.isAvailable) {
45 napi_value err;
46 NAPI_CALL(env, napi_create_string_utf8(env, "The lock is acquired", NAPI_AUTO_LENGTH, &err));
47 napi_reject_deferred(env, deferred, err);
48 } else {
49 LockRequest *lockRequest =
50 new LockRequest(this, AsyncLockManager::GetCurrentTid(env), env, cb, mode, options, deferred);
51 std::unique_lock<std::mutex> lock(asyncLockMutex_);
52 pendingList_.push_back(lockRequest);
53 ProcessPendingLockRequestUnsafe(env, lockRequest);
54 }
55 return promise;
56 }
57
CleanUpLockRequestOnCompletion(LockRequest * lockRequest)58 void AsyncLock::CleanUpLockRequestOnCompletion(LockRequest* lockRequest)
59 {
60 std::unique_lock<std::mutex> lock(asyncLockMutex_);
61 auto it = std::find(heldList_.begin(), heldList_.end(), lockRequest);
62 if (it == heldList_.end()) {
63 HILOG_FATAL("Lock is not found");
64 return;
65 }
66 heldList_.erase(it);
67 if (heldList_.empty()) {
68 // There are may be other shared lock requests in the heldList_.
69 // IF so, we mustn't change the status.
70 lockStatus_ = LOCK_MODE_UNLOCK;
71 }
72 napi_env env = lockRequest->GetEnv();
73 delete lockRequest;
74 if (pendingList_.empty()) {
75 if (refCount_ == 0 && heldList_.empty()) {
76 lock.unlock();
77 AsyncLockManager::CheckAndRemoveLock(this);
78 }
79 return;
80 }
81 ProcessPendingLockRequestUnsafe(env);
82 }
83
CleanUpLockRequest(LockRequest * lockRequest)84 bool AsyncLock::CleanUpLockRequest(LockRequest *lockRequest)
85 {
86 std::unique_lock<std::mutex> lock(asyncLockMutex_);
87 auto it = std::find(pendingList_.begin(), pendingList_.end(), lockRequest);
88 if (it == pendingList_.end()) {
89 // the lock got held while we were waiting on the mutex, no-op
90 return false;
91 }
92 // we won the race, need to remove the request from the queue and handle the time out event
93 pendingList_.erase(it);
94 return true;
95 }
96
97 template <bool isAsync>
ProcessLockRequest(napi_env env,LockRequest * lockRequest)98 void AsyncLock::ProcessLockRequest(napi_env env, LockRequest *lockRequest)
99 {
100 heldList_.push_back(lockRequest);
101 pendingList_.pop_front();
102 asyncLockMutex_.unlock();
103 if constexpr (isAsync) {
104 lockRequest->CallCallbackAsync();
105 } else {
106 lockRequest->CallCallback();
107 }
108 asyncLockMutex_.lock();
109 }
110
ProcessPendingLockRequest(napi_env env,LockRequest * syncLockRequest)111 void AsyncLock::ProcessPendingLockRequest(napi_env env, LockRequest* syncLockRequest)
112 {
113 std::unique_lock<std::mutex> lock(asyncLockMutex_);
114 if (pendingList_.empty()) {
115 if (refCount_ == 0 && heldList_.empty()) {
116 lock.unlock();
117 AsyncLockManager::CheckAndRemoveLock(this);
118 }
119 return;
120 }
121 ProcessPendingLockRequestUnsafe(env, syncLockRequest);
122 }
123
ProcessPendingLockRequestUnsafe(napi_env env,LockRequest * syncLockRequest)124 void AsyncLock::ProcessPendingLockRequestUnsafe(napi_env env, LockRequest *syncLockRequest)
125 {
126 LockRequest *lockRequest = pendingList_.front();
127 if (!CanAcquireLockUnsafe(lockRequest->GetMode())) {
128 return;
129 }
130 lockStatus_ = lockRequest->GetMode();
131 if (lockStatus_ == LOCK_MODE_SHARED) {
132 do {
133 if (syncLockRequest == lockRequest) {
134 ProcessLockRequest<false>(env, lockRequest);
135 } else {
136 ProcessLockRequest<true>(env, lockRequest);
137 }
138 if (pendingList_.empty()) {
139 break;
140 }
141 lockRequest = pendingList_.front();
142 } while (lockRequest->GetMode() == LOCK_MODE_SHARED);
143 } else {
144 ProcessLockRequest<true>(env, lockRequest);
145 }
146 }
147
CanAcquireLockUnsafe(LockMode mode)148 bool AsyncLock::CanAcquireLockUnsafe(LockMode mode)
149 {
150 if (heldList_.empty()) {
151 return true;
152 }
153 if (mode == LOCK_MODE_SHARED && lockStatus_ == LOCK_MODE_SHARED) {
154 return true;
155 }
156 if (lockStatus_ == LOCK_MODE_UNLOCK) {
157 return true;
158 }
159 return false;
160 }
161
CanAcquireLock(LockMode mode)162 bool AsyncLock::CanAcquireLock(LockMode mode)
163 {
164 std::unique_lock<std::mutex> lock(asyncLockMutex_);
165 return CanAcquireLockUnsafe(mode);
166 }
167
FillLockState(napi_env env,napi_value held,napi_value pending)168 napi_status AsyncLock::FillLockState(napi_env env, napi_value held, napi_value pending)
169 {
170 std::unique_lock<std::mutex> lock(asyncLockMutex_);
171 uint32_t idx = 0;
172 for (LockRequest *rq : heldList_) {
173 napi_value info = CreateLockInfo(env, rq);
174 bool pendingException = false;
175 napi_is_exception_pending(env, &pendingException);
176 if (pendingException) {
177 return napi_pending_exception;
178 }
179 napi_value index;
180 napi_create_int32(env, idx, &index);
181 napi_status status = napi_set_property(env, held, index, info);
182 if (status != napi_ok) {
183 return status;
184 }
185 ++idx;
186 }
187 idx = 0;
188 for (LockRequest *rq : pendingList_) {
189 napi_value info = CreateLockInfo(env, rq);
190 bool pendingException = false;
191 napi_is_exception_pending(env, &pendingException);
192 if (pendingException) {
193 return napi_pending_exception;
194 }
195 napi_value index;
196 napi_create_int32(env, idx, &index);
197 napi_status status = napi_set_property(env, pending, index, info);
198 if (status != napi_ok) {
199 return status;
200 }
201 ++idx;
202 }
203 return napi_ok;
204 }
205
CreateLockInfo(napi_env env,const LockRequest * rq)206 napi_value AsyncLock::CreateLockInfo(napi_env env, const LockRequest *rq)
207 {
208 napi_value info;
209 NAPI_CALL(env, napi_create_object(env, &info));
210 napi_value name;
211 NAPI_CALL(env, napi_create_string_utf8(env, lockName_.c_str(), NAPI_AUTO_LENGTH, &name));
212 napi_value mode;
213 NAPI_CALL(env, napi_create_int32(env, rq->GetMode(), &mode));
214 napi_value tid;
215 NAPI_CALL(env, napi_create_int32(env, rq->GetTid(), &tid));
216
217 napi_property_descriptor properties[] = {
218 DECLARE_NAPI_PROPERTY("name", name),
219 DECLARE_NAPI_PROPERTY("mode", mode),
220 DECLARE_NAPI_PROPERTY("contextId", tid),
221 };
222 NAPI_CALL(env, napi_define_properties(env, info, sizeof(properties) / sizeof(properties[0]), properties));
223 return info;
224 }
225
IsReadyForDeletion()226 bool AsyncLock::IsReadyForDeletion()
227 {
228 std::unique_lock<std::mutex> lock(asyncLockMutex_);
229 return refCount_ == 0 && pendingList_.empty() && heldList_.empty();
230 }
231
IncRefCount()232 uint32_t AsyncLock::IncRefCount()
233 {
234 std::unique_lock<std::mutex> lock(asyncLockMutex_);
235 return ++refCount_;
236 }
237
DecRefCount()238 uint32_t AsyncLock::DecRefCount()
239 {
240 std::unique_lock<std::mutex> lock(asyncLockMutex_);
241 return --refCount_;
242 }
243
GetSatisfiedRequestInfos()244 std::vector<RequestCreationInfo> AsyncLock::GetSatisfiedRequestInfos()
245 {
246 std::vector<RequestCreationInfo> result;
247 std::unique_lock<std::mutex> lock(asyncLockMutex_);
248 for (auto *request : heldList_) {
249 result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
250 }
251 return result;
252 }
253
GetPendingRequestInfos()254 std::vector<RequestCreationInfo> AsyncLock::GetPendingRequestInfos()
255 {
256 std::vector<RequestCreationInfo> result;
257 std::unique_lock<std::mutex> lock(asyncLockMutex_);
258 for (auto *request : pendingList_) {
259 result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
260 }
261 return result;
262 }
263
264 } // namespace Commonlibrary::Concurrent::LocksModule
265