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 <uv.h>
17 #include "async_lock.h"
18 #include "async_lock_manager.h"
19 #include "helper/error_helper.h"
20 #include "helper/napi_helper.h"
21 #include "helper/object_helper.h"
22 #include "tools/log.h"
23
24 namespace Commonlibrary::Concurrent::LocksModule {
25 using namespace Commonlibrary::Concurrent::Common::Helper;
26
AsyncLock(const std::string & lockName)27 AsyncLock::AsyncLock(const std::string &lockName)
28 {
29 lockName_ = lockName;
30 anonymousLockId_ = 0;
31 }
32
AsyncLock(uint32_t lockId)33 AsyncLock::AsyncLock(uint32_t lockId)
34 {
35 lockName_ = "";
36 anonymousLockId_ = lockId;
37 }
38
LockAsync(napi_env env,napi_ref cb,LockMode mode,const LockOptions & options)39 napi_value AsyncLock::LockAsync(napi_env env, napi_ref cb, LockMode mode, const LockOptions &options)
40 {
41 napi_value promise;
42 napi_deferred deferred;
43 napi_create_promise(env, &deferred, &promise);
44 LockRequest *lockRequest =
45 new LockRequest(this, AsyncLockManager::GetCurrentTid(env), env, cb, mode, options, deferred);
46 std::unique_lock<std::mutex> lock(asyncLockMutex_);
47 if (!CanAcquireLock(lockRequest) && options.isAvailable) {
48 napi_value err;
49 NAPI_CALL(env, napi_create_string_utf8(env, "The lock is acquired", NAPI_AUTO_LENGTH, &err));
50 napi_reject_deferred(env, deferred, err);
51 } else {
52 lockRequest->OnQueued(env, options.timeoutMillis);
53 pendingList_.push_back(lockRequest);
54 ProcessPendingLockRequestUnsafe(env, lockRequest);
55 }
56 return promise;
57 }
58
CleanUpLockRequestOnCompletion(LockRequest * lockRequest)59 void AsyncLock::CleanUpLockRequestOnCompletion(LockRequest* lockRequest)
60 {
61 std::unique_lock<std::mutex> lock(asyncLockMutex_);
62 auto it = std::find(heldList_.begin(), heldList_.end(), lockRequest);
63 if (it == heldList_.end()) {
64 HILOG_FATAL("Lock is not found");
65 return;
66 }
67 heldList_.erase(it);
68 if (heldList_.empty()) {
69 // There are may be other shared lock requests in the heldList_.
70 // IF so, we mustn't change the status.
71 lockStatus_ = LOCK_MODE_UNLOCK;
72 }
73 napi_env env = lockRequest->GetEnv();
74 delete lockRequest;
75 ProcessPendingLockRequestUnsafe(env);
76 }
77
CleanUpLockRequestOnTimeout(LockRequest * lockRequest)78 bool AsyncLock::CleanUpLockRequestOnTimeout(LockRequest* lockRequest)
79 {
80 std::unique_lock<std::mutex> lock(asyncLockMutex_);
81 auto it = std::find(pendingList_.begin(), pendingList_.end(), lockRequest);
82 if (it == pendingList_.end()) {
83 // the lock got held while we were waiting on the mutex, no-op
84 return false;
85 }
86 // we won the race, need to remove the request from the queue and handle the time out event
87 pendingList_.erase(it);
88 return true;
89 }
90
91 template <bool isAsync>
ProcessLockRequest(napi_env env,LockRequest * lockRequest)92 void AsyncLock::ProcessLockRequest(napi_env env, LockRequest *lockRequest)
93 {
94 lockRequest->OnSatisfied(env);
95 heldList_.push_back(lockRequest);
96 pendingList_.pop_front();
97 asyncLockMutex_.unlock();
98 if constexpr (isAsync) {
99 lockRequest->CallCallbackAsync();
100 } else {
101 lockRequest->CallCallback();
102 }
103 asyncLockMutex_.lock();
104 }
105
ProcessPendingLockRequest(napi_env env,LockRequest * syncLockRequest)106 void AsyncLock::ProcessPendingLockRequest(napi_env env, LockRequest* syncLockRequest)
107 {
108 std::unique_lock<std::mutex> lock(asyncLockMutex_);
109 ProcessPendingLockRequestUnsafe(env, syncLockRequest);
110 }
111
ProcessPendingLockRequestUnsafe(napi_env env,LockRequest * syncLockRequest)112 void AsyncLock::ProcessPendingLockRequestUnsafe(napi_env env, LockRequest* syncLockRequest)
113 {
114 if (pendingList_.empty()) {
115 if (refCount_ == 0 && heldList_.empty()) {
116 // No more refs to the lock. We need to delete the instance but we cannot do it right now
117 // because asyncLockMutex_ is acquired. Do it asynchronously.
118 AsyncDestroy(env);
119 }
120 return;
121 }
122 LockRequest *lockRequest = pendingList_.front();
123 if (!CanAcquireLock(lockRequest)) {
124 return;
125 }
126 lockStatus_ = lockRequest->GetMode();
127 if (lockStatus_ == LOCK_MODE_SHARED) {
128 do {
129 if (syncLockRequest == lockRequest) {
130 ProcessLockRequest<false>(env, lockRequest);
131 } else {
132 ProcessLockRequest<true>(env, lockRequest);
133 }
134 if (pendingList_.empty()) {
135 break;
136 }
137 lockRequest = pendingList_.front();
138 } while (lockRequest->GetMode() == LOCK_MODE_SHARED);
139 } else {
140 ProcessLockRequest<true>(env, lockRequest);
141 }
142 }
143
CanAcquireLock(LockRequest * lockRequest)144 bool AsyncLock::CanAcquireLock(LockRequest *lockRequest)
145 {
146 if (heldList_.empty()) {
147 return true;
148 }
149 if (lockRequest->GetMode() == LOCK_MODE_SHARED && lockStatus_ == LOCK_MODE_SHARED) {
150 return true;
151 }
152 if (lockStatus_ == LOCK_MODE_UNLOCK) {
153 return true;
154 }
155 return false;
156 }
157
FillLockState(napi_env env,napi_value held,napi_value pending)158 napi_status AsyncLock::FillLockState(napi_env env, napi_value held, napi_value pending)
159 {
160 std::unique_lock<std::mutex> lock(asyncLockMutex_);
161 uint32_t idx = 0;
162 for (LockRequest *rq : heldList_) {
163 napi_value info = CreateLockInfo(env, rq);
164 bool pendingException = false;
165 napi_is_exception_pending(env, &pendingException);
166 if (pendingException) {
167 return napi_pending_exception;
168 }
169 napi_value index;
170 napi_create_int32(env, idx, &index);
171 napi_status status = napi_set_property(env, held, index, info);
172 if (status != napi_ok) {
173 return status;
174 }
175 ++idx;
176 }
177 idx = 0;
178 for (LockRequest *rq : pendingList_) {
179 napi_value info = CreateLockInfo(env, rq);
180 bool pendingException = false;
181 napi_is_exception_pending(env, &pendingException);
182 if (pendingException) {
183 return napi_pending_exception;
184 }
185 napi_value index;
186 napi_create_int32(env, idx, &index);
187 napi_status status = napi_set_property(env, pending, index, info);
188 if (status != napi_ok) {
189 return status;
190 }
191 ++idx;
192 }
193 return napi_ok;
194 }
195
CreateLockInfo(napi_env env,const LockRequest * rq)196 napi_value AsyncLock::CreateLockInfo(napi_env env, const LockRequest *rq)
197 {
198 napi_value info;
199 NAPI_CALL(env, napi_create_object(env, &info));
200 napi_value name;
201 NAPI_CALL(env, napi_create_string_utf8(env, lockName_.c_str(), NAPI_AUTO_LENGTH, &name));
202 napi_value mode;
203 NAPI_CALL(env, napi_create_int32(env, rq->GetMode(), &mode));
204 napi_value tid;
205 NAPI_CALL(env, napi_create_int32(env, rq->GetTid(), &tid));
206
207 napi_property_descriptor properties[] = {
208 DECLARE_NAPI_PROPERTY("name", name),
209 DECLARE_NAPI_PROPERTY("mode", mode),
210 DECLARE_NAPI_PROPERTY("contextId", tid),
211 };
212 NAPI_CALL(env, napi_define_properties(env, info, sizeof(properties) / sizeof(properties[0]), properties));
213 return info;
214 }
215
AsyncDestroy(napi_env env)216 void AsyncLock::AsyncDestroy(napi_env env)
217 {
218 napi_value resourceName;
219 napi_create_string_utf8(env, "AsyncLock::AsyncDestroyCallback", NAPI_AUTO_LENGTH, &resourceName);
220 auto *data = new std::pair<AsyncLock *, napi_async_work>();
221 data->first = this;
222 napi_async_work &work = data->second;
223 napi_status status = napi_create_async_work(
224 env, nullptr, resourceName, [](napi_env, void *) {}, AsyncDestroyCallback, data, &work);
225 if (status != napi_ok) {
226 HILOG_FATAL("Internal error: cannot create async work");
227 }
228 status = napi_queue_async_work(env, work);
229 if (status != napi_ok) {
230 HILOG_FATAL("Internal error: cannot queue async work");
231 }
232 }
233
AsyncDestroyCallback(napi_env env,napi_status,void * data)234 void AsyncLock::AsyncDestroyCallback(napi_env env, napi_status, void *data)
235 {
236 auto *lockAndWork = reinterpret_cast<std::pair<AsyncLock *, napi_async_work> *>(data);
237 delete lockAndWork->first;
238 napi_delete_async_work(env, lockAndWork->second);
239 delete lockAndWork;
240 }
241
IncRefCount()242 uint32_t AsyncLock::IncRefCount()
243 {
244 std::unique_lock<std::mutex> lock(asyncLockMutex_);
245 return ++refCount_;
246 }
247
DecRefCount()248 uint32_t AsyncLock::DecRefCount()
249 {
250 asyncLockMutex_.lock();
251 uint32_t count = --refCount_;
252 if (count == 0) {
253 // No refs to the instance. We can delete it right now if there are no more lock requests.
254 // In case there are some lock requests, the last processed lock request will delete the instance.
255 if (pendingList_.size() == 0 && heldList_.size() == 0) {
256 asyncLockMutex_.unlock();
257 delete this;
258 return 0;
259 }
260 }
261 asyncLockMutex_.unlock();
262 return count;
263 }
264
GetSatisfiedRequestInfos()265 std::vector<RequestCreationInfo> AsyncLock::GetSatisfiedRequestInfos()
266 {
267 std::vector<RequestCreationInfo> result;
268 std::unique_lock<std::mutex> lock(asyncLockMutex_);
269 for (auto *request : heldList_) {
270 result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
271 }
272 return result;
273 }
274
GetPendingRequestInfos()275 std::vector<RequestCreationInfo> AsyncLock::GetPendingRequestInfos()
276 {
277 std::vector<RequestCreationInfo> result;
278 std::unique_lock<std::mutex> lock(asyncLockMutex_);
279 for (auto *request : pendingList_) {
280 result.push_back(RequestCreationInfo { request->GetTid(), request->GetCreationStacktrace() });
281 }
282 return result;
283 }
284
285 } // namespace Commonlibrary::Concurrent::LocksModule
286