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 <new>
26
27 #include <fmq/EventFlag.h>
28 #include <utils/Log.h>
29 #include <utils/SystemClock.h>
30
31 namespace android {
32 namespace hardware {
33
createEventFlag(int fd,off_t offset,EventFlag ** flag)34 status_t EventFlag::createEventFlag(int fd, off_t offset, EventFlag** flag) {
35 if (flag == nullptr) {
36 return BAD_VALUE;
37 }
38
39 status_t status = NO_MEMORY;
40 *flag = nullptr;
41
42 EventFlag* evFlag = new (std::nothrow) EventFlag(fd, offset, &status);
43 if (evFlag != nullptr) {
44 if (status == NO_ERROR) {
45 *flag = evFlag;
46 } else {
47 delete evFlag;
48 }
49 }
50
51 return status;
52 }
53
createEventFlag(std::atomic<uint32_t> * fwAddr,EventFlag ** flag)54 status_t EventFlag::createEventFlag(std::atomic<uint32_t>* fwAddr,
55 EventFlag** flag) {
56 if (flag == nullptr) {
57 return BAD_VALUE;
58 }
59
60 status_t status = NO_MEMORY;
61 *flag = nullptr;
62
63 EventFlag* evFlag = new (std::nothrow) EventFlag(fwAddr, &status);
64 if (evFlag != nullptr) {
65 if (status == NO_ERROR) {
66 *flag = evFlag;
67 } else {
68 delete evFlag;
69 }
70 }
71
72 return status;
73 }
74
75 /*
76 * mmap memory for the futex word
77 */
EventFlag(int fd,off_t offset,status_t * status)78 EventFlag::EventFlag(int fd, off_t offset, status_t* status) {
79 mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL,
80 sizeof(std::atomic<uint32_t>),
81 PROT_READ | PROT_WRITE,
82 MAP_SHARED, fd, offset));
83 mEfWordNeedsUnmapping = true;
84 if (mEfWordPtr != MAP_FAILED) {
85 *status = NO_ERROR;
86 } else {
87 *status = -errno;
88 ALOGE("Attempt to mmap event flag word failed: %s\n", strerror(errno));
89 }
90 }
91
92 /*
93 * Use this constructor if we already know where the futex word for
94 * the EventFlag group lives.
95 */
EventFlag(std::atomic<uint32_t> * fwAddr,status_t * status)96 EventFlag::EventFlag(std::atomic<uint32_t>* fwAddr, status_t* status) {
97 *status = NO_ERROR;
98 if (fwAddr == nullptr) {
99 *status = BAD_VALUE;
100 } else {
101 mEfWordPtr = fwAddr;
102 }
103 }
104
105 /*
106 * Set the specified bits of the futex word here and wake up any
107 * thread waiting on any of the bits.
108 */
wake(uint32_t bitmask)109 status_t EventFlag::wake(uint32_t bitmask) {
110 /*
111 * Return early if there are no set bits in bitmask.
112 */
113 if (bitmask == 0) {
114 return NO_ERROR;
115 }
116
117 status_t status = NO_ERROR;
118 uint32_t old = std::atomic_fetch_or(mEfWordPtr, bitmask);
119 /*
120 * No need to call FUTEX_WAKE_BITSET if there were deferred wakes
121 * already available for all set bits from bitmask.
122 */
123 if ((~old & bitmask) != 0) {
124 int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET,
125 INT_MAX, NULL, NULL, bitmask);
126 if (ret == -1) {
127 status = -errno;
128 ALOGE("Error in event flag wake attempt: %s\n", strerror(errno));
129 }
130 }
131 return status;
132 }
133
134 /*
135 * Wait for any of the bits in the bitmask to be set
136 * and return which bits caused the return.
137 */
waitHelper(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds)138 status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
139 /*
140 * Return early if there are no set bits in bitmask.
141 */
142 if (bitmask == 0 || efState == nullptr) {
143 return BAD_VALUE;
144 }
145
146 status_t status = NO_ERROR;
147 uint32_t old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
148 uint32_t setBits = old & bitmask;
149 /*
150 * If there was a deferred wake available, no need to call FUTEX_WAIT_BITSET.
151 */
152 if (setBits != 0) {
153 *efState = setBits;
154 return status;
155 }
156
157 uint32_t efWord = old & ~bitmask;
158 /*
159 * The syscall will put the thread to sleep only
160 * if the futex word still contains the expected
161 * value i.e. efWord. If the futex word contents have
162 * changed, it fails with the error EAGAIN; If a timeout
163 * is specified and exceeded the syscall fails with ETIMEDOUT.
164 */
165 int ret = 0;
166 if (timeoutNanoSeconds) {
167 struct timespec waitTimeAbsolute;
168 addNanosecondsToCurrentTime(timeoutNanoSeconds, &waitTimeAbsolute);
169
170 ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET,
171 efWord, &waitTimeAbsolute, NULL, bitmask);
172 } else {
173 ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET, efWord, NULL, NULL, bitmask);
174 }
175 if (ret == -1) {
176 status = -errno;
177 if (status != -EAGAIN && status != -ETIMEDOUT) {
178 ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno));
179 }
180 *efState = 0;
181 } else {
182 old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
183 *efState = old & bitmask;
184
185 if (*efState == 0) {
186 /* Return -EINTR for a spurious wakeup */
187 status = -EINTR;
188 }
189 }
190 return status;
191 }
192
193 /*
194 * Wait for any of the bits in the bitmask to be set
195 * and return which bits caused the return. If 'retry'
196 * is true, wait again on a spurious wake-up.
197 */
wait(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds,bool retry)198 status_t EventFlag::wait(uint32_t bitmask,
199 uint32_t* efState,
200 int64_t timeoutNanoSeconds,
201 bool retry) {
202 if (!retry) {
203 return waitHelper(bitmask, efState, timeoutNanoSeconds);
204 }
205
206 bool shouldTimeOut = timeoutNanoSeconds != 0;
207 int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
208 status_t status;
209 while (true) {
210 if (shouldTimeOut) {
211 int64_t currentTimeNs = android::elapsedRealtimeNano();
212 /*
213 * Decrement TimeOutNanos to account for the time taken to complete the last
214 * iteration of the while loop.
215 */
216 timeoutNanoSeconds -= currentTimeNs - prevTimeNs;
217 prevTimeNs = currentTimeNs;
218 if (timeoutNanoSeconds <= 0) {
219 status = -ETIMEDOUT;
220 *efState = 0;
221 break;
222 }
223 }
224
225 status = waitHelper(bitmask, efState, timeoutNanoSeconds);
226 if ((status != -EAGAIN) && (status != -EINTR)) {
227 break;
228 }
229 }
230 return status;
231 }
232
unmapEventFlagWord(std::atomic<uint32_t> * efWordPtr,bool * efWordNeedsUnmapping)233 status_t EventFlag::unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr,
234 bool* efWordNeedsUnmapping) {
235 status_t status = NO_ERROR;
236 if (*efWordNeedsUnmapping) {
237 int ret = munmap(efWordPtr, sizeof(std::atomic<uint32_t>));
238 if (ret != 0) {
239 status = -errno;
240 ALOGE("Error in deleting event flag group: %s\n", strerror(errno));
241 }
242 *efWordNeedsUnmapping = false;
243 }
244 return status;
245 }
246
deleteEventFlag(EventFlag ** evFlag)247 status_t EventFlag::deleteEventFlag(EventFlag** evFlag) {
248 if (evFlag == nullptr || *evFlag == nullptr) {
249 return BAD_VALUE;
250 }
251
252 status_t status = unmapEventFlagWord((*evFlag)->mEfWordPtr,
253 &(*evFlag)->mEfWordNeedsUnmapping);
254 delete *evFlag;
255 *evFlag = nullptr;
256
257 return status;
258 }
259
addNanosecondsToCurrentTime(int64_t nanoSeconds,struct timespec * waitTime)260 void EventFlag::addNanosecondsToCurrentTime(int64_t nanoSeconds, struct timespec* waitTime) {
261 static constexpr int64_t kNanosPerSecond = 1000000000;
262
263 clock_gettime(CLOCK_MONOTONIC, waitTime);
264 waitTime->tv_sec += nanoSeconds / kNanosPerSecond;
265 waitTime->tv_nsec += nanoSeconds % kNanosPerSecond;
266
267 if (waitTime->tv_nsec >= kNanosPerSecond) {
268 waitTime->tv_sec++;
269 waitTime->tv_nsec -= kNanosPerSecond;
270 }
271 }
272
~EventFlag()273 EventFlag::~EventFlag() {
274 unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
275 }
276
277 } // namespace hardware
278 } // namespace android
279