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