• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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