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 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, 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 */ 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 /* 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 */ 135status_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 */ 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 230status_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 257void 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 270EventFlag::~EventFlag() { 271 unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping); 272} 273 274} // namespace hardware 275} // namespace android 276