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