/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "FMQ_EventFlags" #include <fmq/EventFlag.h> #include <linux/futex.h> #include <sys/mman.h> #include <sys/syscall.h> #include <unistd.h> #include <utils/Log.h> #include <utils/SystemClock.h> #include <new> namespace android { namespace hardware { status_t EventFlag::createEventFlag(int fd, off_t offset, EventFlag** flag) { if (flag == nullptr) { return BAD_VALUE; } status_t status = NO_MEMORY; *flag = nullptr; EventFlag* evFlag = new (std::nothrow) EventFlag(fd, offset, &status); if (evFlag != nullptr) { if (status == NO_ERROR) { *flag = evFlag; } else { delete evFlag; } } return status; } status_t EventFlag::createEventFlag(std::atomic<uint32_t>* fwAddr, EventFlag** flag) { if (flag == nullptr) { return BAD_VALUE; } status_t status = NO_MEMORY; *flag = nullptr; EventFlag* evFlag = new (std::nothrow) EventFlag(fwAddr, &status); if (evFlag != nullptr) { if (status == NO_ERROR) { *flag = evFlag; } else { delete evFlag; } } return status; } /* * mmap memory for the futex word */ EventFlag::EventFlag(int fd, off_t offset, status_t* status) { mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL, sizeof(std::atomic<uint32_t>), PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)); mEfWordNeedsUnmapping = true; if (mEfWordPtr != MAP_FAILED) { *status = NO_ERROR; } else { *status = -errno; ALOGE("Attempt to mmap event flag word failed: %s\n", strerror(errno)); } } /* * Use this constructor if we already know where the futex word for * the EventFlag group lives. */ EventFlag::EventFlag(std::atomic<uint32_t>* fwAddr, status_t* status) { *status = NO_ERROR; if (fwAddr == nullptr) { *status = BAD_VALUE; } else { mEfWordPtr = fwAddr; } } /* * Set the specified bits of the futex word here and wake up any * thread waiting on any of the bits. */ status_t EventFlag::wake(uint32_t bitmask) { /* * Return early if there are no set bits in bitmask. */ if (bitmask == 0) { return NO_ERROR; } status_t status = NO_ERROR; uint32_t old = std::atomic_fetch_or(mEfWordPtr, bitmask); /* * No need to call FUTEX_WAKE_BITSET if there were deferred wakes * already available for all set bits from bitmask. */ if ((~old & bitmask) != 0) { int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET, INT_MAX, NULL, NULL, bitmask); if (ret == -1) { status = -errno; ALOGE("Error in event flag wake attempt: %s\n", strerror(errno)); } } return status; } /* * Wait for any of the bits in the bitmask to be set * and return which bits caused the return. */ status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) { /* * Return early if there are no set bits in bitmask. */ if (bitmask == 0 || efState == nullptr) { return BAD_VALUE; } status_t status = NO_ERROR; uint32_t old = std::atomic_fetch_and(mEfWordPtr, ~bitmask); uint32_t setBits = old & bitmask; /* * If there was a deferred wake available, no need to call FUTEX_WAIT_BITSET. */ if (setBits != 0) { *efState = setBits; return status; } uint32_t efWord = old & ~bitmask; /* * The syscall will put the thread to sleep only * if the futex word still contains the expected * value i.e. efWord. If the futex word contents have * changed, it fails with the error EAGAIN; If a timeout * is specified and exceeded the syscall fails with ETIMEDOUT. */ int ret = 0; if (timeoutNanoSeconds) { struct timespec waitTimeAbsolute; addNanosecondsToCurrentTime(timeoutNanoSeconds, &waitTimeAbsolute); ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET, efWord, &waitTimeAbsolute, NULL, bitmask); } else { ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET, efWord, NULL, NULL, bitmask); } if (ret == -1) { status = -errno; if (status != -EAGAIN && status != -ETIMEDOUT) { ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno)); } *efState = 0; } else { old = std::atomic_fetch_and(mEfWordPtr, ~bitmask); *efState = old & bitmask; if (*efState == 0) { /* Return -EINTR for a spurious wakeup */ status = -EINTR; } } return status; } /* * Wait for any of the bits in the bitmask to be set * and return which bits caused the return. If 'retry' * is true, wait again on a spurious wake-up. */ status_t EventFlag::wait(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds, bool retry) { if (!retry) { return waitHelper(bitmask, efState, timeoutNanoSeconds); } bool shouldTimeOut = timeoutNanoSeconds != 0; int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0; status_t status; while (true) { if (shouldTimeOut) { int64_t currentTimeNs = android::elapsedRealtimeNano(); /* * Decrement TimeOutNanos to account for the time taken to complete the last * iteration of the while loop. */ timeoutNanoSeconds -= currentTimeNs - prevTimeNs; prevTimeNs = currentTimeNs; if (timeoutNanoSeconds <= 0) { status = -ETIMEDOUT; *efState = 0; break; } } status = waitHelper(bitmask, efState, timeoutNanoSeconds); if ((status != -EAGAIN) && (status != -EINTR)) { break; } } return status; } status_t EventFlag::unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr, bool* efWordNeedsUnmapping) { status_t status = NO_ERROR; if (*efWordNeedsUnmapping) { int ret = munmap(efWordPtr, sizeof(std::atomic<uint32_t>)); if (ret != 0) { status = -errno; ALOGE("Error in deleting event flag group: %s\n", strerror(errno)); } *efWordNeedsUnmapping = false; } return status; } status_t EventFlag::deleteEventFlag(EventFlag** evFlag) { if (evFlag == nullptr || *evFlag == nullptr) { return BAD_VALUE; } status_t status = unmapEventFlagWord((*evFlag)->mEfWordPtr, &(*evFlag)->mEfWordNeedsUnmapping); delete *evFlag; *evFlag = nullptr; return status; } void EventFlag::addNanosecondsToCurrentTime(int64_t nanoSeconds, struct timespec* waitTime) { static constexpr int64_t kNanosPerSecond = 1000000000; clock_gettime(CLOCK_MONOTONIC, waitTime); waitTime->tv_sec += nanoSeconds / kNanosPerSecond; waitTime->tv_nsec += nanoSeconds % kNanosPerSecond; if (waitTime->tv_nsec >= kNanosPerSecond) { waitTime->tv_sec++; waitTime->tv_nsec -= kNanosPerSecond; } } EventFlag::~EventFlag() { unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping); } } // namespace hardware } // namespace android