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 <fmq/EventFlag.h>
20 #include <linux/futex.h>
21 #include <sys/mman.h>
22 #include <sys/syscall.h>
23 #include <unistd.h>
24 #include <utils/Log.h>
25 #include <utils/SystemClock.h>
26 #include <new>
27
28 namespace android {
29 namespace hardware {
30
createEventFlag(int fd,off_t offset,EventFlag ** flag)31 status_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
createEventFlag(std::atomic<uint32_t> * fwAddr,EventFlag ** flag)51 status_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 */
EventFlag(int fd,off_t offset,status_t * status)75 EventFlag::EventFlag(int fd, off_t offset, status_t* status) {
76 mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL,
77 sizeof(std::atomic<uint32_t>),
78 PROT_READ | PROT_WRITE,
79 MAP_SHARED, fd, offset));
80 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 */
EventFlag(std::atomic<uint32_t> * fwAddr,status_t * status)93 EventFlag::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 */
wake(uint32_t bitmask)106 status_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 /*
117 * No need to call FUTEX_WAKE_BITSET if there were deferred wakes
118 * already available for all set bits from bitmask.
119 */
120 if ((~old & bitmask) != 0) {
121 int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET,
122 INT_MAX, NULL, NULL, bitmask);
123 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 */
waitHelper(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds)135 status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
136 /*
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
159 * changed, it fails with the error EAGAIN; If a timeout
160 * is specified and exceeded the syscall fails with ETIMEDOUT.
161 */
162 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 }
172 if (ret == -1) {
173 status = -errno;
174 if (status != -EAGAIN && status != -ETIMEDOUT) {
175 ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno));
176 }
177 *efState = 0;
178 } else {
179 old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
180 *efState = old & bitmask;
181
182 if (*efState == 0) {
183 /* Return -EINTR for a spurious wakeup */
184 status = -EINTR;
185 }
186 }
187 return status;
188 }
189
190 /*
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 */
wait(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds,bool retry)195 status_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
unmapEventFlagWord(std::atomic<uint32_t> * efWordPtr,bool * efWordNeedsUnmapping)230 status_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
deleteEventFlag(EventFlag ** evFlag)244 status_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
addNanosecondsToCurrentTime(int64_t nanoSeconds,struct timespec * waitTime)257 void 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
~EventFlag()270 EventFlag::~EventFlag() {
271 unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
272 }
273
274 } // namespace hardware
275 } // namespace android
276