1 /*
2 * Copyright (c) 2023 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 <unistd.h>
17 #include "cpp/mutex.h"
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE
20 #endif
21
22 #include <map>
23 #include <functional>
24 #include "sync/sync.h"
25 #include "eu/co_routine.h"
26 #include "internal_inc/osal.h"
27 #include "internal_inc/types.h"
28 #include "sync/mutex_private.h"
29 #include "dfx/log/ffrt_log_api.h"
30 #include "ffrt_trace.h"
31 #include "tm/cpu_task.h"
32
33 namespace ffrt {
try_lock()34 bool mutexPrivate::try_lock()
35 {
36 int v = sync_detail::UNLOCK;
37 bool ret = l.compare_exchange_strong(v, sync_detail::LOCK, std::memory_order_acquire, std::memory_order_relaxed);
38 #ifdef FFRT_MUTEX_DEADLOCK_CHECK
39 if (ret) {
40 uint64_t task = ExecuteCtx::Cur()->task ? reinterpret_cast<uint64_t>(ExecuteCtx::Cur()->task) : GetTid();
41 MutexGraph::Instance().AddNode(task, 0, false);
42 owner.store(task, std::memory_order_relaxed);
43 }
44 #endif
45 return ret;
46 }
47
lock()48 void mutexPrivate::lock()
49 {
50 #ifdef FFRT_MUTEX_DEADLOCK_CHECK
51 uint64_t task;
52 uint64_t ownerTask;
53 task = ExecuteCtx::Cur()->task ? reinterpret_cast<uint64_t>(ExecuteCtx::Cur()->task) : GetTid();
54 ownerTask = owner.load(std::memory_order_relaxed);
55 if (ownerTask) {
56 MutexGraph::Instance().AddNode(task, ownerTask, true);
57 } else {
58 MutexGraph::Instance().AddNode(task, 0, false);
59 }
60 #endif
61 int v = sync_detail::UNLOCK;
62 if (l.compare_exchange_strong(v, sync_detail::LOCK, std::memory_order_acquire, std::memory_order_relaxed)) {
63 goto lock_out;
64 }
65 if (l.load(std::memory_order_relaxed) == sync_detail::WAIT) {
66 wait();
67 }
68 while (l.exchange(sync_detail::WAIT, std::memory_order_acquire) != sync_detail::UNLOCK) {
69 wait();
70 }
71
72 lock_out:
73 #ifdef FFRT_MUTEX_DEADLOCK_CHECK
74 owner.store(task, std::memory_order_relaxed);
75 #endif
76 return;
77 }
78
try_lock()79 bool RecursiveMutexPrivate::try_lock()
80 {
81 auto ctx = ExecuteCtx::Cur();
82 auto task = ctx->task;
83 if ((!USE_COROUTINE) || (task == nullptr)) {
84 fMutex.lock();
85 if (taskLockNums.first == UINT64_MAX) {
86 fMutex.unlock();
87 if (mt.try_lock()) {
88 fMutex.lock();
89 taskLockNums = std::make_pair(GetTid(), 1);
90 fMutex.unlock();
91 return true;
92 } else {
93 return false;
94 }
95 }
96
97 if (taskLockNums.first == GetTid()) {
98 taskLockNums.second += 1;
99 fMutex.unlock();
100 return true;
101 }
102
103 fMutex.unlock();
104 return false;
105 }
106
107 fMutex.lock();
108 if (taskLockNums.first == UINT64_MAX) {
109 fMutex.unlock();
110 if (mt.try_lock()) {
111 fMutex.lock();
112 taskLockNums = std::make_pair(task->gid | 0x8000000000000000, 1);
113 fMutex.unlock();
114 return true;
115 } else {
116 return false;
117 }
118 }
119
120 if (taskLockNums.first == (task->gid | 0x8000000000000000)) {
121 taskLockNums.second += 1;
122 fMutex.unlock();
123 return true;
124 }
125
126 fMutex.unlock();
127 return false;
128 }
129
lock()130 void RecursiveMutexPrivate::lock()
131 {
132 auto ctx = ExecuteCtx::Cur();
133 auto task = ctx->task;
134 if ((!USE_COROUTINE) || (task == nullptr)) {
135 fMutex.lock();
136 if (taskLockNums.first != GetTid()) {
137 fMutex.unlock();
138 mt.lock();
139 fMutex.lock();
140 taskLockNums = std::make_pair(GetTid(), 1);
141 fMutex.unlock();
142 return;
143 }
144
145 taskLockNums.second += 1;
146 fMutex.unlock();
147 return;
148 }
149
150 fMutex.lock();
151 if (taskLockNums.first != (task->gid | 0x8000000000000000)) {
152 fMutex.unlock();
153 mt.lock();
154 fMutex.lock();
155 taskLockNums = std::make_pair(task->gid | 0x8000000000000000, 1);
156 fMutex.unlock();
157 return;
158 }
159
160 taskLockNums.second += 1;
161 fMutex.unlock();
162 }
163
unlock()164 void RecursiveMutexPrivate::unlock()
165 {
166 auto ctx = ExecuteCtx::Cur();
167 auto task = ctx->task;
168 if ((!USE_COROUTINE) || (task == nullptr)) {
169 fMutex.lock();
170 if (taskLockNums.first != GetTid()) {
171 fMutex.unlock();
172 return;
173 }
174
175 if (taskLockNums.second == 1) {
176 taskLockNums = std::make_pair(UINT64_MAX, 0);
177 fMutex.unlock();
178 mt.unlock();
179 return;
180 }
181
182 taskLockNums.second -= 1;
183 fMutex.unlock();
184 return;
185 }
186
187 fMutex.lock();
188 if (taskLockNums.first != (task->gid | 0x8000000000000000)) {
189 fMutex.unlock();
190 return;
191 }
192
193 if (taskLockNums.second == 1) {
194 taskLockNums = std::make_pair(UINT64_MAX, 0);
195 fMutex.unlock();
196 mt.unlock();
197 return;
198 }
199
200 taskLockNums.second -= 1;
201 fMutex.unlock();
202 }
203
unlock()204 void mutexPrivate::unlock()
205 {
206 #ifdef FFRT_MUTEX_DEADLOCK_CHECK
207 uint64_t ownerTask = owner.load(std::memory_order_relaxed);
208 owner.store(0, std::memory_order_relaxed);
209 MutexGraph::Instance().RemoveNode(ownerTask);
210 #endif
211 if (l.exchange(sync_detail::UNLOCK, std::memory_order_release) == sync_detail::WAIT) {
212 wake();
213 }
214 }
215
wait()216 void mutexPrivate::wait()
217 {
218 auto ctx = ExecuteCtx::Cur();
219 auto task = ctx->task;
220 if (ThreadWaitMode(task)) {
221 wlock.lock();
222 if (l.load(std::memory_order_relaxed) != sync_detail::WAIT) {
223 wlock.unlock();
224 return;
225 }
226 list.PushBack(ctx->wn.node);
227 std::unique_lock<std::mutex> lk(ctx->wn.wl);
228 if (FFRT_UNLIKELY(LegacyMode(task))) {
229 task->blockType = BlockType::BLOCK_THREAD;
230 ctx->wn.task = task;
231 }
232 wlock.unlock();
233 ctx->wn.cv.wait(lk);
234 return;
235 } else {
236 FFRT_BLOCK_TRACER(task->gid, mtx);
237 CoWait([this](CPUEUTask* task) -> bool {
238 wlock.lock();
239 if (l.load(std::memory_order_relaxed) != sync_detail::WAIT) {
240 wlock.unlock();
241 return false;
242 }
243 list.PushBack(task->fq_we.node);
244 wlock.unlock();
245 // The ownership of the task belongs to ReadyTaskQueue, and the task cannot be accessed any more.
246 return true;
247 });
248 }
249 }
250
wake()251 void mutexPrivate::wake()
252 {
253 wlock.lock();
254 if (list.Empty()) {
255 wlock.unlock();
256 return;
257 }
258 WaitEntry* we = list.PopFront(&WaitEntry::node);
259 if (we == nullptr) {
260 wlock.unlock();
261 return;
262 }
263 CPUEUTask* task = we->task;
264 if (ThreadNotifyMode(task) || we->weType == 2) {
265 WaitUntilEntry* wue = static_cast<WaitUntilEntry*>(we);
266 std::unique_lock lk(wue->wl);
267 if (BlockThread(task)) {
268 task->blockType = BlockType::BLOCK_COROUTINE;
269 we->task = nullptr;
270 }
271 wlock.unlock();
272 wue->cv.notify_one();
273 } else {
274 wlock.unlock();
275 CoRoutineFactory::CoWakeFunc(task, false);
276 }
277 }
278 } // namespace ffrt
279
280 #ifdef __cplusplus
281 extern "C" {
282 #endif
283 API_ATTRIBUTE((visibility("default")))
284 int ffrt_mutexattr_init(ffrt_mutexattr_t* attr)
285 {
286 if (attr == nullptr) {
287 FFRT_LOGE("attr should not be empty");
288 return ffrt_error_inval;
289 }
290 attr->storage = static_cast<long>(ffrt_mutex_default);
291 return ffrt_success;
292 }
293
294 API_ATTRIBUTE((visibility("default")))
295 int ffrt_mutexattr_settype(ffrt_mutexattr_t* attr, int type)
296 {
297 if (attr == nullptr) {
298 FFRT_LOGE("attr should not be empty");
299 return ffrt_error_inval;
300 }
301 if (type != ffrt_mutex_normal && type != ffrt_mutex_recursive && type != ffrt_mutex_default) {
302 FFRT_LOGE("mutex type is invaild");
303 return ffrt_error_inval;
304 }
305 attr->storage = static_cast<long>(type);
306 return ffrt_success;
307 }
308
309 API_ATTRIBUTE((visibility("default")))
310 int ffrt_mutexattr_gettype(ffrt_mutexattr_t* attr, int* type)
311 {
312 if (attr == nullptr || type == nullptr) {
313 FFRT_LOGE("attr or type should not be empty");
314 return ffrt_error_inval;
315 }
316 *type = static_cast<int>(attr->storage);
317 return ffrt_success;
318 }
319
320 API_ATTRIBUTE((visibility("default")))
321 int ffrt_mutexattr_destroy(ffrt_mutexattr_t* attr)
322 {
323 if (attr == nullptr) {
324 FFRT_LOGE("attr should not be empty");
325 return ffrt_error_inval;
326 }
327 return ffrt_success;
328 }
329
330 API_ATTRIBUTE((visibility("default")))
331 int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr)
332 {
333 if (!mutex) {
334 FFRT_LOGE("mutex should not be empty");
335 return ffrt_error_inval;
336 }
337 if (attr == nullptr || attr->storage == static_cast<long>(ffrt_mutex_normal)) {
338 static_assert(sizeof(ffrt::mutexPrivate) <= ffrt_mutex_storage_size,
339 "size must be less than ffrt_mutex_storage_size");
340 new (mutex)ffrt::mutexPrivate();
341 return ffrt_success;
342 } else if (attr->storage == static_cast<long>(ffrt_mutex_recursive)) {
343 static_assert(sizeof(ffrt::RecursiveMutexPrivate) <= ffrt_mutex_storage_size,
344 "size must be less than ffrt_mutex_storage_size");
345 new (mutex)ffrt::RecursiveMutexPrivate();
346 return ffrt_success;
347 }
348 return ffrt_error_inval;
349 }
350
351 API_ATTRIBUTE((visibility("default")))
352 int ffrt_mutex_lock(ffrt_mutex_t* mutex)
353 {
354 if (!mutex) {
355 FFRT_LOGE("mutex should not be empty");
356 return ffrt_error_inval;
357 }
358 auto p = reinterpret_cast<ffrt::mutexBase*>(mutex);
359 p->lock();
360 return ffrt_success;
361 }
362
363 API_ATTRIBUTE((visibility("default")))
364 int ffrt_mutex_unlock(ffrt_mutex_t* mutex)
365 {
366 if (!mutex) {
367 FFRT_LOGE("mutex should not be empty");
368 return ffrt_error_inval;
369 }
370 auto p = reinterpret_cast<ffrt::mutexBase*>(mutex);
371 p->unlock();
372 return ffrt_success;
373 }
374
375 API_ATTRIBUTE((visibility("default")))
376 int ffrt_mutex_trylock(ffrt_mutex_t* mutex)
377 {
378 if (!mutex) {
379 FFRT_LOGE("mutex should not be empty");
380 return ffrt_error_inval;
381 }
382 auto p = reinterpret_cast<ffrt::mutexBase*>(mutex);
383 return p->try_lock() ? ffrt_success : ffrt_error_busy;
384 }
385
386 API_ATTRIBUTE((visibility("default")))
387 int ffrt_mutex_destroy(ffrt_mutex_t* mutex)
388 {
389 if (!mutex) {
390 FFRT_LOGE("mutex should not be empty");
391 return ffrt_error_inval;
392 }
393 auto p = reinterpret_cast<ffrt::mutexBase*>(mutex);
394 p->~mutexBase();
395 return ffrt_success;
396 }
397
398 #ifdef __cplusplus
399 }
400 #endif
401