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