123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- /*
- * 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 <linux/futex.h>
- #include <string.h>
- #include <sys/mman.h>
- #include <sys/syscall.h>
- #include <unistd.h>
- #include <new>
- #include <fmq/EventFlag.h>
- #include <utils/Log.h>
- #include <utils/SystemClock.h>
- 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
|