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