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