| /* |
| * 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 |