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 <limits> 26 #include <new> 27 28 #include <fmq/EventFlag.h> 29 #include <utils/Log.h> 30 #include <utils/SystemClock.h> 31 32 namespace android { 33 namespace hardware { 34 35 status_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 55 status_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 */ 79 EventFlag::EventFlag(int fd, off_t offset, status_t* status) { 80 mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL, 81 sizeof(std::atomic<uint32_t>), 82 PROT_READ | PROT_WRITE, 83 MAP_SHARED, fd, offset)); 84 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 */ 97 EventFlag::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 */ 110 status_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 /* 121 * No need to call FUTEX_WAKE_BITSET if there were deferred wakes 122 * already available for all set bits from bitmask. 123 */ 124 constexpr size_t kIntMax = std::numeric_limits<int>::max(); 125 if ((~old & bitmask) != 0) { 126 int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET, kIntMax, NULL, NULL, bitmask); 127 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 */ 139 status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) { 140 /* 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 163 * changed, it fails with the error EAGAIN; If a timeout 164 * is specified and exceeded the syscall fails with ETIMEDOUT. 165 */ 166 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 } 176 if (ret == -1) { 177 status = -errno; 178 if (status != -EAGAIN && status != -ETIMEDOUT) { 179 ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno)); 180 } 181 *efState = 0; 182 } else { 183 old = std::atomic_fetch_and(mEfWordPtr, ~bitmask); 184 *efState = old & bitmask; 185 186 if (*efState == 0) { 187 /* Return -EINTR for a spurious wakeup */ 188 status = -EINTR; 189 } 190 } 191 return status; 192 } 193 194 /* 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 */ 199 status_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 234 status_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 248 status_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 261 void 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 274 EventFlag::~EventFlag() { 275 unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping); 276 } 277 278 } // namespace hardware 279 } // namespace android 280