You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
8.0 KiB
275 lines
8.0 KiB
/* |
|
* 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
|
|
|