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 #ifndef LOG_TAG
16 #define LOG_TAG "FutexTool"
17 #endif
18
19 #include "futex_tool.h"
20
21 #include <cinttypes>
22 #include <ctime>
23 #include "linux/futex.h"
24 #include <sys/syscall.h>
25
26 #include "audio_errors.h"
27 #include "audio_service_log.h"
28 #include "audio_utils.h"
29
30 namespace OHOS {
31 namespace AudioStandard {
32 namespace {
33 const int32_t WAIT_TRY_COUNT = 50;
34 const int64_t SEC_TO_NANOSEC = 1000000000;
35 }
36 // FUTEX_WAIT using relative timeout value.
TimeoutToRelativeTime(int64_t timeout,struct timespec & realtime)37 void TimeoutToRelativeTime(int64_t timeout, struct timespec &realtime)
38 {
39 int64_t timeoutNanoSec = timeout % SEC_TO_NANOSEC;
40 int64_t timeoutSec = timeout / SEC_TO_NANOSEC;
41
42 realtime.tv_nsec = timeoutNanoSec;
43 realtime.tv_sec = timeoutSec;
44 }
45
FutexWait(std::atomic<uint32_t> * futexPtr,int64_t timeout)46 FutexCode FutexTool::FutexWait(std::atomic<uint32_t> *futexPtr, int64_t timeout)
47 {
48 CHECK_AND_RETURN_RET_LOG(futexPtr != nullptr, FUTEX_INVALID_PARAMS, "futexPtr is null");
49 Trace trace("FutexTool::FutexWait");
50 uint32_t current = futexPtr->load();
51 if (current != IS_READY && current != IS_NOT_READY && current != IS_PRE_EXIT) {
52 AUDIO_ERR_LOG("failed: invalid param:%{public}u", current);
53 return FUTEX_INVALID_PARAMS;
54 }
55 struct timespec waitTime;
56 if (timeout > 0) {
57 TimeoutToRelativeTime(timeout, waitTime);
58 }
59
60 uint32_t expect = IS_READY;
61 if (!futexPtr->compare_exchange_strong(expect, IS_NOT_READY)) {
62 if (expect == IS_PRE_EXIT) {
63 AUDIO_ERR_LOG("failed with invalid status:%{public}u", expect);
64 return FUTEX_OPERATION_FAILED;
65 }
66 AUDIO_WARNING_LOG("recall while futex value is IS_NOT_READY");
67 }
68 long res = 0;
69 int32_t tryCount = 0;
70 while (tryCount < WAIT_TRY_COUNT) {
71 if (futexPtr->load() == IS_PRE_EXIT) {
72 AUDIO_INFO_LOG("pre_exit is called!");
73 return FUTEX_PRE_EXIT;
74 }
75 res = syscall(__NR_futex, futexPtr, FUTEX_WAIT, IS_NOT_READY, (timeout <= 0 ? NULL : &waitTime), NULL, 0);
76 if (res == 0 && futexPtr->load() == IS_READY) {
77 return FUTEX_SUCCESS; // return success here
78 }
79 if (errno == ETIMEDOUT) {
80 AUDIO_WARNING_LOG("wait:%{public}" PRId64"ns timeout, result:%{public}ld errno[%{public}d]:%{public}s",
81 timeout, res, errno, strerror(errno));
82 return FUTEX_TIMEOUT;
83 }
84 if (errno != EAGAIN) {
85 AUDIO_WARNING_LOG("result:%{public}ld, errno[%{public}d]:%{public}s", res, errno, strerror(errno));
86 return FUTEX_OPERATION_FAILED;
87 }
88 tryCount++;
89 }
90 if (tryCount >= WAIT_TRY_COUNT) {
91 AUDIO_ERR_LOG("too much spurious wake-up");
92 }
93 return FUTEX_OPERATION_FAILED;
94 }
95
FutexWake(std::atomic<uint32_t> * futexPtr,uint32_t wakeVal)96 FutexCode FutexTool::FutexWake(std::atomic<uint32_t> *futexPtr, uint32_t wakeVal)
97 {
98 CHECK_AND_RETURN_RET_LOG(futexPtr != nullptr, FUTEX_INVALID_PARAMS, "futexPtr is null");
99 Trace trace("FutexTool::FutexWake");
100 uint32_t current = futexPtr->load();
101 if (current != IS_READY && current != IS_NOT_READY && current != IS_PRE_EXIT) {
102 AUDIO_ERR_LOG("failed: invalid param:%{public}u", current);
103 return FUTEX_INVALID_PARAMS;
104 }
105 if (wakeVal == IS_PRE_EXIT) {
106 futexPtr->store(IS_PRE_EXIT);
107 syscall(__NR_futex, futexPtr, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
108 return FUTEX_SUCCESS;
109 }
110 uint32_t expect = IS_NOT_READY;
111 if (futexPtr->compare_exchange_strong(expect, IS_READY)) {
112 long res = syscall(__NR_futex, futexPtr, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
113 if (res < 0) {
114 AUDIO_ERR_LOG("failed:%{public}ld, errno[%{public}d]:%{public}s", res, errno, strerror(errno));
115 return FUTEX_OPERATION_FAILED;
116 }
117 }
118 return FUTEX_SUCCESS;
119 }
120 } // namespace AudioStandard
121 } // namespace OHOS
122