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 <linux/futex.h> 20 #include <string.h> 21 #include <sys/mman.h> 22 #include <sys/syscall.h> 23 #include <unistd.h> 24 25 #include <new> 26 27 #include <fmq/EventFlag.h> 28 #include <utils/Log.h> 29 #include <utils/SystemClock.h> 30 31 namespace android { 32 namespace hardware { 33 34 status_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 54 status_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 */ 78 EventFlag::EventFlag(int fd, off_t offset, status_t* status) { 79 mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL, 80 sizeof(std::atomic<uint32_t>), 81 PROT_READ | PROT_WRITE, 82 MAP_SHARED, fd, offset)); 83 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 */ 96 EventFlag::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 */ 109 status_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 /* 120 * No need to call FUTEX_WAKE_BITSET if there were deferred wakes 121 * already available for all set bits from bitmask. 122 */ 123 if ((~old & bitmask) != 0) { 124 int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET, 125 INT_MAX, NULL, NULL, bitmask); 126 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 */ 138 status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) { 139 /* 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 162 * changed, it fails with the error EAGAIN; If a timeout 163 * is specified and exceeded the syscall fails with ETIMEDOUT. 164 */ 165 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 } 175 if (ret == -1) { 176 status = -errno; 177 if (status != -EAGAIN && status != -ETIMEDOUT) { 178 ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno)); 179 } 180 *efState = 0; 181 } else { 182 old = std::atomic_fetch_and(mEfWordPtr, ~bitmask); 183 *efState = old & bitmask; 184 185 if (*efState == 0) { 186 /* Return -EINTR for a spurious wakeup */ 187 status = -EINTR; 188 } 189 } 190 return status; 191 } 192 193 /* 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 */ 198 status_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 233 status_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 247 status_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 260 void 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 273 EventFlag::~EventFlag() { 274 unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping); 275 } 276 277 } // namespace hardware 278 } // namespace android 279