xref: /aosp_15_r20/system/libfmq/EventFlag.cpp (revision be431cd81a9a2349eaea34eb56fcf6d1608da596)
1*be431cd8SAndroid Build Coastguard Worker /*
2*be431cd8SAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*be431cd8SAndroid Build Coastguard Worker  *
4*be431cd8SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*be431cd8SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*be431cd8SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*be431cd8SAndroid Build Coastguard Worker  *
8*be431cd8SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*be431cd8SAndroid Build Coastguard Worker  *
10*be431cd8SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*be431cd8SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*be431cd8SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*be431cd8SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*be431cd8SAndroid Build Coastguard Worker  * limitations under the License.
15*be431cd8SAndroid Build Coastguard Worker  */
16*be431cd8SAndroid Build Coastguard Worker 
17*be431cd8SAndroid Build Coastguard Worker #define LOG_TAG "FMQ_EventFlags"
18*be431cd8SAndroid Build Coastguard Worker 
19*be431cd8SAndroid Build Coastguard Worker #include <linux/futex.h>
20*be431cd8SAndroid Build Coastguard Worker #include <string.h>
21*be431cd8SAndroid Build Coastguard Worker #include <sys/mman.h>
22*be431cd8SAndroid Build Coastguard Worker #include <sys/syscall.h>
23*be431cd8SAndroid Build Coastguard Worker #include <unistd.h>
24*be431cd8SAndroid Build Coastguard Worker 
25*be431cd8SAndroid Build Coastguard Worker #include <limits>
26*be431cd8SAndroid Build Coastguard Worker #include <new>
27*be431cd8SAndroid Build Coastguard Worker 
28*be431cd8SAndroid Build Coastguard Worker #include <fmq/EventFlag.h>
29*be431cd8SAndroid Build Coastguard Worker #include <utils/Log.h>
30*be431cd8SAndroid Build Coastguard Worker #include <utils/SystemClock.h>
31*be431cd8SAndroid Build Coastguard Worker 
32*be431cd8SAndroid Build Coastguard Worker namespace android {
33*be431cd8SAndroid Build Coastguard Worker namespace hardware {
34*be431cd8SAndroid Build Coastguard Worker 
createEventFlag(std::atomic<uint32_t> * fwAddr,EventFlag ** flag)35*be431cd8SAndroid Build Coastguard Worker status_t EventFlag::createEventFlag(std::atomic<uint32_t>* fwAddr,
36*be431cd8SAndroid Build Coastguard Worker                                     EventFlag** flag) {
37*be431cd8SAndroid Build Coastguard Worker     if (flag == nullptr) {
38*be431cd8SAndroid Build Coastguard Worker         return BAD_VALUE;
39*be431cd8SAndroid Build Coastguard Worker     }
40*be431cd8SAndroid Build Coastguard Worker 
41*be431cd8SAndroid Build Coastguard Worker     status_t status = NO_MEMORY;
42*be431cd8SAndroid Build Coastguard Worker     *flag  = nullptr;
43*be431cd8SAndroid Build Coastguard Worker 
44*be431cd8SAndroid Build Coastguard Worker     EventFlag* evFlag = new (std::nothrow) EventFlag(fwAddr, &status);
45*be431cd8SAndroid Build Coastguard Worker     if (evFlag != nullptr) {
46*be431cd8SAndroid Build Coastguard Worker         if (status == NO_ERROR) {
47*be431cd8SAndroid Build Coastguard Worker             *flag = evFlag;
48*be431cd8SAndroid Build Coastguard Worker         } else {
49*be431cd8SAndroid Build Coastguard Worker             delete evFlag;
50*be431cd8SAndroid Build Coastguard Worker         }
51*be431cd8SAndroid Build Coastguard Worker     }
52*be431cd8SAndroid Build Coastguard Worker 
53*be431cd8SAndroid Build Coastguard Worker     return status;
54*be431cd8SAndroid Build Coastguard Worker }
55*be431cd8SAndroid Build Coastguard Worker 
56*be431cd8SAndroid Build Coastguard Worker /*
57*be431cd8SAndroid Build Coastguard Worker  * Use this constructor if we already know where the futex word for
58*be431cd8SAndroid Build Coastguard Worker  * the EventFlag group lives.
59*be431cd8SAndroid Build Coastguard Worker  */
EventFlag(std::atomic<uint32_t> * fwAddr,status_t * status)60*be431cd8SAndroid Build Coastguard Worker EventFlag::EventFlag(std::atomic<uint32_t>* fwAddr, status_t* status) {
61*be431cd8SAndroid Build Coastguard Worker     *status = NO_ERROR;
62*be431cd8SAndroid Build Coastguard Worker     if (fwAddr == nullptr) {
63*be431cd8SAndroid Build Coastguard Worker         *status = BAD_VALUE;
64*be431cd8SAndroid Build Coastguard Worker     } else {
65*be431cd8SAndroid Build Coastguard Worker         mEfWordPtr = fwAddr;
66*be431cd8SAndroid Build Coastguard Worker     }
67*be431cd8SAndroid Build Coastguard Worker }
68*be431cd8SAndroid Build Coastguard Worker 
69*be431cd8SAndroid Build Coastguard Worker /*
70*be431cd8SAndroid Build Coastguard Worker  * Set the specified bits of the futex word here and wake up any
71*be431cd8SAndroid Build Coastguard Worker  * thread waiting on any of the bits.
72*be431cd8SAndroid Build Coastguard Worker  */
wake(uint32_t bitmask)73*be431cd8SAndroid Build Coastguard Worker status_t EventFlag::wake(uint32_t bitmask) {
74*be431cd8SAndroid Build Coastguard Worker     /*
75*be431cd8SAndroid Build Coastguard Worker      * Return early if there are no set bits in bitmask.
76*be431cd8SAndroid Build Coastguard Worker      */
77*be431cd8SAndroid Build Coastguard Worker     if (bitmask == 0) {
78*be431cd8SAndroid Build Coastguard Worker         return NO_ERROR;
79*be431cd8SAndroid Build Coastguard Worker     }
80*be431cd8SAndroid Build Coastguard Worker 
81*be431cd8SAndroid Build Coastguard Worker     status_t status = NO_ERROR;
82*be431cd8SAndroid Build Coastguard Worker     uint32_t old = std::atomic_fetch_or(mEfWordPtr, bitmask);
83*be431cd8SAndroid Build Coastguard Worker     /*
84*be431cd8SAndroid Build Coastguard Worker      * No need to call FUTEX_WAKE_BITSET if there were deferred wakes
85*be431cd8SAndroid Build Coastguard Worker      * already available for all set bits from bitmask.
86*be431cd8SAndroid Build Coastguard Worker      */
87*be431cd8SAndroid Build Coastguard Worker     constexpr size_t kIntMax = std::numeric_limits<int>::max();
88*be431cd8SAndroid Build Coastguard Worker     if ((~old & bitmask) != 0) {
89*be431cd8SAndroid Build Coastguard Worker         int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET, kIntMax, NULL, NULL, bitmask);
90*be431cd8SAndroid Build Coastguard Worker         if (ret == -1) {
91*be431cd8SAndroid Build Coastguard Worker             status = -errno;
92*be431cd8SAndroid Build Coastguard Worker             ALOGE("Error in event flag wake attempt: %s\n", strerror(errno));
93*be431cd8SAndroid Build Coastguard Worker         }
94*be431cd8SAndroid Build Coastguard Worker     }
95*be431cd8SAndroid Build Coastguard Worker     return status;
96*be431cd8SAndroid Build Coastguard Worker }
97*be431cd8SAndroid Build Coastguard Worker 
98*be431cd8SAndroid Build Coastguard Worker /*
99*be431cd8SAndroid Build Coastguard Worker  * Wait for any of the bits in the bitmask to be set
100*be431cd8SAndroid Build Coastguard Worker  * and return which bits caused the return.
101*be431cd8SAndroid Build Coastguard Worker  */
waitHelper(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds)102*be431cd8SAndroid Build Coastguard Worker status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
103*be431cd8SAndroid Build Coastguard Worker     /*
104*be431cd8SAndroid Build Coastguard Worker      * Return early if there are no set bits in bitmask.
105*be431cd8SAndroid Build Coastguard Worker      */
106*be431cd8SAndroid Build Coastguard Worker     if (bitmask == 0 || efState == nullptr) {
107*be431cd8SAndroid Build Coastguard Worker         return BAD_VALUE;
108*be431cd8SAndroid Build Coastguard Worker     }
109*be431cd8SAndroid Build Coastguard Worker 
110*be431cd8SAndroid Build Coastguard Worker     status_t status = NO_ERROR;
111*be431cd8SAndroid Build Coastguard Worker     uint32_t old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
112*be431cd8SAndroid Build Coastguard Worker     uint32_t setBits = old & bitmask;
113*be431cd8SAndroid Build Coastguard Worker     /*
114*be431cd8SAndroid Build Coastguard Worker      * If there was a deferred wake available, no need to call FUTEX_WAIT_BITSET.
115*be431cd8SAndroid Build Coastguard Worker      */
116*be431cd8SAndroid Build Coastguard Worker     if (setBits != 0) {
117*be431cd8SAndroid Build Coastguard Worker         *efState = setBits;
118*be431cd8SAndroid Build Coastguard Worker         return status;
119*be431cd8SAndroid Build Coastguard Worker     }
120*be431cd8SAndroid Build Coastguard Worker 
121*be431cd8SAndroid Build Coastguard Worker     uint32_t efWord = old & ~bitmask;
122*be431cd8SAndroid Build Coastguard Worker     /*
123*be431cd8SAndroid Build Coastguard Worker      * The syscall will put the thread to sleep only
124*be431cd8SAndroid Build Coastguard Worker      * if the futex word still contains the expected
125*be431cd8SAndroid Build Coastguard Worker      * value i.e. efWord. If the futex word contents have
126*be431cd8SAndroid Build Coastguard Worker      * changed, it fails with the error EAGAIN; If a timeout
127*be431cd8SAndroid Build Coastguard Worker      * is specified and exceeded the syscall fails with ETIMEDOUT.
128*be431cd8SAndroid Build Coastguard Worker      */
129*be431cd8SAndroid Build Coastguard Worker     int ret = 0;
130*be431cd8SAndroid Build Coastguard Worker     if (timeoutNanoSeconds) {
131*be431cd8SAndroid Build Coastguard Worker         struct timespec waitTimeAbsolute;
132*be431cd8SAndroid Build Coastguard Worker         addNanosecondsToCurrentTime(timeoutNanoSeconds, &waitTimeAbsolute);
133*be431cd8SAndroid Build Coastguard Worker 
134*be431cd8SAndroid Build Coastguard Worker         ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET,
135*be431cd8SAndroid Build Coastguard Worker                       efWord, &waitTimeAbsolute, NULL, bitmask);
136*be431cd8SAndroid Build Coastguard Worker     } else {
137*be431cd8SAndroid Build Coastguard Worker         ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET, efWord, NULL, NULL, bitmask);
138*be431cd8SAndroid Build Coastguard Worker     }
139*be431cd8SAndroid Build Coastguard Worker     if (ret == -1) {
140*be431cd8SAndroid Build Coastguard Worker         status = -errno;
141*be431cd8SAndroid Build Coastguard Worker         if (status != -EAGAIN && status != -ETIMEDOUT) {
142*be431cd8SAndroid Build Coastguard Worker             ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno));
143*be431cd8SAndroid Build Coastguard Worker         }
144*be431cd8SAndroid Build Coastguard Worker         *efState = 0;
145*be431cd8SAndroid Build Coastguard Worker     } else {
146*be431cd8SAndroid Build Coastguard Worker         old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
147*be431cd8SAndroid Build Coastguard Worker         *efState = old & bitmask;
148*be431cd8SAndroid Build Coastguard Worker 
149*be431cd8SAndroid Build Coastguard Worker         if (*efState == 0) {
150*be431cd8SAndroid Build Coastguard Worker             /* Return -EINTR for a spurious wakeup */
151*be431cd8SAndroid Build Coastguard Worker             status = -EINTR;
152*be431cd8SAndroid Build Coastguard Worker         }
153*be431cd8SAndroid Build Coastguard Worker     }
154*be431cd8SAndroid Build Coastguard Worker     return status;
155*be431cd8SAndroid Build Coastguard Worker }
156*be431cd8SAndroid Build Coastguard Worker 
157*be431cd8SAndroid Build Coastguard Worker /*
158*be431cd8SAndroid Build Coastguard Worker  * Wait for any of the bits in the bitmask to be set
159*be431cd8SAndroid Build Coastguard Worker  * and return which bits caused the return. If 'retry'
160*be431cd8SAndroid Build Coastguard Worker  * is true, wait again on a spurious wake-up.
161*be431cd8SAndroid Build Coastguard Worker  */
wait(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds,bool retry)162*be431cd8SAndroid Build Coastguard Worker status_t EventFlag::wait(uint32_t bitmask,
163*be431cd8SAndroid Build Coastguard Worker                          uint32_t* efState,
164*be431cd8SAndroid Build Coastguard Worker                          int64_t timeoutNanoSeconds,
165*be431cd8SAndroid Build Coastguard Worker                          bool retry) {
166*be431cd8SAndroid Build Coastguard Worker     if (!retry) {
167*be431cd8SAndroid Build Coastguard Worker         return waitHelper(bitmask, efState, timeoutNanoSeconds);
168*be431cd8SAndroid Build Coastguard Worker     }
169*be431cd8SAndroid Build Coastguard Worker 
170*be431cd8SAndroid Build Coastguard Worker     bool shouldTimeOut = timeoutNanoSeconds != 0;
171*be431cd8SAndroid Build Coastguard Worker     int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
172*be431cd8SAndroid Build Coastguard Worker     status_t status;
173*be431cd8SAndroid Build Coastguard Worker     while (true) {
174*be431cd8SAndroid Build Coastguard Worker         status = waitHelper(bitmask, efState, timeoutNanoSeconds);
175*be431cd8SAndroid Build Coastguard Worker         if ((status != -EAGAIN) && (status != -EINTR)) {
176*be431cd8SAndroid Build Coastguard Worker             break;
177*be431cd8SAndroid Build Coastguard Worker         }
178*be431cd8SAndroid Build Coastguard Worker 
179*be431cd8SAndroid Build Coastguard Worker         if (shouldTimeOut) {
180*be431cd8SAndroid Build Coastguard Worker             int64_t currentTimeNs = android::elapsedRealtimeNano();
181*be431cd8SAndroid Build Coastguard Worker             /*
182*be431cd8SAndroid Build Coastguard Worker              * Decrement TimeOutNanos to account for the time taken to complete the last
183*be431cd8SAndroid Build Coastguard Worker              * iteration of the while loop.
184*be431cd8SAndroid Build Coastguard Worker              */
185*be431cd8SAndroid Build Coastguard Worker             timeoutNanoSeconds -= currentTimeNs - prevTimeNs;
186*be431cd8SAndroid Build Coastguard Worker             prevTimeNs = currentTimeNs;
187*be431cd8SAndroid Build Coastguard Worker             if (timeoutNanoSeconds <= 0) {
188*be431cd8SAndroid Build Coastguard Worker                 status = -ETIMEDOUT;
189*be431cd8SAndroid Build Coastguard Worker                 *efState = 0;
190*be431cd8SAndroid Build Coastguard Worker                 break;
191*be431cd8SAndroid Build Coastguard Worker             }
192*be431cd8SAndroid Build Coastguard Worker         }
193*be431cd8SAndroid Build Coastguard Worker     }
194*be431cd8SAndroid Build Coastguard Worker     return status;
195*be431cd8SAndroid Build Coastguard Worker }
196*be431cd8SAndroid Build Coastguard Worker 
unmapEventFlagWord(std::atomic<uint32_t> * efWordPtr,bool * efWordNeedsUnmapping)197*be431cd8SAndroid Build Coastguard Worker status_t EventFlag::unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr,
198*be431cd8SAndroid Build Coastguard Worker                                        bool* efWordNeedsUnmapping) {
199*be431cd8SAndroid Build Coastguard Worker     status_t status = NO_ERROR;
200*be431cd8SAndroid Build Coastguard Worker     if (*efWordNeedsUnmapping) {
201*be431cd8SAndroid Build Coastguard Worker         int ret = munmap(efWordPtr, sizeof(std::atomic<uint32_t>));
202*be431cd8SAndroid Build Coastguard Worker         if (ret != 0) {
203*be431cd8SAndroid Build Coastguard Worker             status = -errno;
204*be431cd8SAndroid Build Coastguard Worker             ALOGE("Error in deleting event flag group: %s\n", strerror(errno));
205*be431cd8SAndroid Build Coastguard Worker         }
206*be431cd8SAndroid Build Coastguard Worker         *efWordNeedsUnmapping = false;
207*be431cd8SAndroid Build Coastguard Worker     }
208*be431cd8SAndroid Build Coastguard Worker     return status;
209*be431cd8SAndroid Build Coastguard Worker }
210*be431cd8SAndroid Build Coastguard Worker 
deleteEventFlag(EventFlag ** evFlag)211*be431cd8SAndroid Build Coastguard Worker status_t EventFlag::deleteEventFlag(EventFlag** evFlag) {
212*be431cd8SAndroid Build Coastguard Worker     if (evFlag == nullptr || *evFlag == nullptr) {
213*be431cd8SAndroid Build Coastguard Worker         return BAD_VALUE;
214*be431cd8SAndroid Build Coastguard Worker     }
215*be431cd8SAndroid Build Coastguard Worker 
216*be431cd8SAndroid Build Coastguard Worker     status_t status = unmapEventFlagWord((*evFlag)->mEfWordPtr,
217*be431cd8SAndroid Build Coastguard Worker                                          &(*evFlag)->mEfWordNeedsUnmapping);
218*be431cd8SAndroid Build Coastguard Worker     delete *evFlag;
219*be431cd8SAndroid Build Coastguard Worker     *evFlag = nullptr;
220*be431cd8SAndroid Build Coastguard Worker 
221*be431cd8SAndroid Build Coastguard Worker     return status;
222*be431cd8SAndroid Build Coastguard Worker }
223*be431cd8SAndroid Build Coastguard Worker 
addNanosecondsToCurrentTime(int64_t nanoSeconds,struct timespec * waitTime)224*be431cd8SAndroid Build Coastguard Worker void EventFlag::addNanosecondsToCurrentTime(int64_t nanoSeconds, struct timespec* waitTime) {
225*be431cd8SAndroid Build Coastguard Worker     static constexpr int64_t kNanosPerSecond = 1000000000;
226*be431cd8SAndroid Build Coastguard Worker 
227*be431cd8SAndroid Build Coastguard Worker     clock_gettime(CLOCK_MONOTONIC, waitTime);
228*be431cd8SAndroid Build Coastguard Worker     waitTime->tv_sec += nanoSeconds / kNanosPerSecond;
229*be431cd8SAndroid Build Coastguard Worker     waitTime->tv_nsec += nanoSeconds % kNanosPerSecond;
230*be431cd8SAndroid Build Coastguard Worker 
231*be431cd8SAndroid Build Coastguard Worker     if (waitTime->tv_nsec >= kNanosPerSecond) {
232*be431cd8SAndroid Build Coastguard Worker         waitTime->tv_sec++;
233*be431cd8SAndroid Build Coastguard Worker         waitTime->tv_nsec -= kNanosPerSecond;
234*be431cd8SAndroid Build Coastguard Worker     }
235*be431cd8SAndroid Build Coastguard Worker }
236*be431cd8SAndroid Build Coastguard Worker 
~EventFlag()237*be431cd8SAndroid Build Coastguard Worker EventFlag::~EventFlag() {
238*be431cd8SAndroid Build Coastguard Worker     unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
239*be431cd8SAndroid Build Coastguard Worker }
240*be431cd8SAndroid Build Coastguard Worker 
241*be431cd8SAndroid Build Coastguard Worker }  // namespace hardware
242*be431cd8SAndroid Build Coastguard Worker }  // namespace android
243