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