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,const std::function<bool (void)> & pred)46 FutexCode FutexTool::FutexWait(std::atomic<uint32_t> *futexPtr, int64_t timeout, const std::function<bool(void)> &pred)
47 {
48 CHECK_AND_RETURN_RET_LOG(futexPtr != nullptr, FUTEX_INVALID_PARAMS, "futexPtr is null");
49 CHECK_AND_RETURN_RET_LOG(pred, FUTEX_INVALID_PARAMS, "pred err");
50 Trace trace("FutexTool::FutexWait");
51 uint32_t current = futexPtr->load();
52 if (current != IS_READY && current != IS_NOT_READY && current != IS_PRE_EXIT) {
53 AUDIO_ERR_LOG("failed: invalid param:%{public}u", current);
54 return FUTEX_INVALID_PARAMS;
55 }
56
57 int64_t timeIn = ClockTime::GetCurNano();
58 int64_t cost = 0;
59 struct timespec waitTime;
60 if (timeout > 0) {
61 TimeoutToRelativeTime(timeout, waitTime);
62 }
63
64 long res = 0;
65 int32_t tryCount = 0;
66 while (tryCount < WAIT_TRY_COUNT) {
67 uint32_t expect = IS_READY;
68 if (!futexPtr->compare_exchange_strong(expect, IS_NOT_READY)) {
69 if (expect == IS_PRE_EXIT) {
70 AUDIO_ERR_LOG("failed with invalid status:%{public}u", expect);
71 return FUTEX_OPERATION_FAILED;
72 }
73 }
74
75 if (pred()) { return FUTEX_SUCCESS; }
76
77 cost = ClockTime::GetCurNano() - timeIn;
78 if (cost >= timeout && timeout > 0) {
79 return FUTEX_TIMEOUT;
80 }
81 if (cost < timeout && timeout > 0) {
82 TimeoutToRelativeTime(timeout - cost, waitTime);
83 }
84
85 Trace traceSyscall(std::string("syscall expect: ") + std::to_string(expect) + "cost: " + std::to_string(cost));
86 res = syscall(__NR_futex, futexPtr, FUTEX_WAIT, IS_NOT_READY, (timeout <= 0 ? NULL : &waitTime), NULL, 0);
87 auto sysErr = errno;
88
89 if (pred()) { return FUTEX_SUCCESS; }
90
91 if ((res != 0) && (sysErr == ETIMEDOUT)) {
92 AUDIO_WARNING_LOG("wait:%{public}" PRId64"ns timeout, result:%{public}ld sysErr[%{public}d]:%{public}s",
93 timeout, res, sysErr, strerror(sysErr));
94 return FUTEX_TIMEOUT;
95 }
96
97 if ((res != 0) && (sysErr != EAGAIN)) {
98 AUDIO_WARNING_LOG("result:%{public}ld, sysErr[%{public}d]:%{public}s", res, sysErr, strerror(sysErr));
99 }
100 tryCount++;
101 }
102 if (tryCount >= WAIT_TRY_COUNT) {
103 AUDIO_ERR_LOG("too much spurious wake-up");
104 }
105 return FUTEX_OPERATION_FAILED;
106 }
107
FutexWake(std::atomic<uint32_t> * futexPtr,uint32_t wakeVal)108 FutexCode FutexTool::FutexWake(std::atomic<uint32_t> *futexPtr, uint32_t wakeVal)
109 {
110 CHECK_AND_RETURN_RET_LOG(futexPtr != nullptr, FUTEX_INVALID_PARAMS, "futexPtr is null");
111 Trace trace("FutexTool::FutexWake");
112 uint32_t current = futexPtr->load();
113 if (current != IS_READY && current != IS_NOT_READY && current != IS_PRE_EXIT) {
114 AUDIO_ERR_LOG("failed: invalid param:%{public}u", current);
115 return FUTEX_INVALID_PARAMS;
116 }
117 if (wakeVal == IS_PRE_EXIT) {
118 futexPtr->store(IS_PRE_EXIT);
119 syscall(__NR_futex, futexPtr, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
120 return FUTEX_SUCCESS;
121 }
122 uint32_t expect = IS_NOT_READY;
123 if (futexPtr->compare_exchange_strong(expect, IS_READY)) {
124 long res = syscall(__NR_futex, futexPtr, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
125 if (res < 0) {
126 AUDIO_ERR_LOG("failed:%{public}ld, errno[%{public}d]:%{public}s", res, errno, strerror(errno));
127 return FUTEX_OPERATION_FAILED;
128 }
129 }
130 return FUTEX_SUCCESS;
131 }
132 } // namespace AudioStandard
133 } // namespace OHOS
134