blob: cb101bd2ecacc9d2fbd067aeff4a5c0d1f89f62b [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
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080019#include <linux/futex.h>
Dan Albert06c7a6f2017-10-11 12:44:12 -070020#include <string.h>
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080021#include <sys/mman.h>
22#include <sys/syscall.h>
Steven Moreland26531e02017-04-13 18:20:19 -070023#include <unistd.h>
Dan Albert06c7a6f2017-10-11 12:44:12 -070024
25#include <new>
26
27#include <fmq/EventFlag.h>
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080028#include <utils/Log.h>
Hridya Valsarajuf542b5a2017-03-21 11:33:33 -070029#include <utils/SystemClock.h>
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080030
31namespace android {
32namespace hardware {
33
34status_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
54status_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 */
78EventFlag::EventFlag(int fd, off_t offset, status_t* status) {
79 mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL,
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -080080 sizeof(std::atomic<uint32_t>),
81 PROT_READ | PROT_WRITE,
82 MAP_SHARED, fd, offset));
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080083 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 */
96EventFlag::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 */
109status_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 /*
Hridya Valsarajuaf605d42017-01-19 12:12:29 -0800120 * No need to call FUTEX_WAKE_BITSET if there were deferred wakes
121 * already available for all set bits from bitmask.
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800122 */
123 if ((~old & bitmask) != 0) {
124 int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET,
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -0800125 INT_MAX, NULL, NULL, bitmask);
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800126 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 */
Hridya Valsarajuf542b5a2017-03-21 11:33:33 -0700138status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800139 /*
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
Andreas Huber3c2dcb12017-01-11 12:40:22 -0800162 * changed, it fails with the error EAGAIN; If a timeout
163 * is specified and exceeded the syscall fails with ETIMEDOUT.
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800164 */
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -0800165 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 }
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800175 if (ret == -1) {
176 status = -errno;
Andreas Huber3c2dcb12017-01-11 12:40:22 -0800177 if (status != -EAGAIN && status != -ETIMEDOUT) {
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800178 ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno));
179 }
180 *efState = 0;
181 } else {
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800182 old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
183 *efState = old & bitmask;
Hridya Valsaraju77c8aba2017-03-03 07:57:37 -0800184
185 if (*efState == 0) {
186 /* Return -EINTR for a spurious wakeup */
187 status = -EINTR;
188 }
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800189 }
190 return status;
191}
192
Hridya Valsarajuf542b5a2017-03-21 11:33:33 -0700193/*
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 */
198status_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
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800233status_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
247status_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
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -0800260void 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
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800273EventFlag::~EventFlag() {
274 unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
275}
276
277} // namespace hardware
278} // namespace android