Home | History | Annotate | Download | only in libfmq
      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 <linux/futex.h>
     20 #include <string.h>
     21 #include <sys/mman.h>
     22 #include <sys/syscall.h>
     23 #include <unistd.h>
     24 
     25 #include <new>
     26 
     27 #include <fmq/EventFlag.h>
     28 #include <utils/Log.h>
     29 #include <utils/SystemClock.h>
     30 
     31 namespace android {
     32 namespace hardware {
     33 
     34 status_t EventFlag::createEventFlag(int fd, off_t offset, EventFlag** flag) {
     35     if (flag == nullptr) {
     36         return BAD_VALUE;
     37     }
     38 
     39     status_t status = NO_MEMORY;
     40     *flag = nullptr;
     41 
     42     EventFlag* evFlag = new (std::nothrow) EventFlag(fd, offset, &status);
     43     if (evFlag != nullptr) {
     44         if (status == NO_ERROR) {
     45             *flag = evFlag;
     46         } else {
     47             delete evFlag;
     48         }
     49     }
     50 
     51     return status;
     52 }
     53 
     54 status_t EventFlag::createEventFlag(std::atomic<uint32_t>* fwAddr,
     55                                     EventFlag** flag) {
     56     if (flag == nullptr) {
     57         return BAD_VALUE;
     58     }
     59 
     60     status_t status = NO_MEMORY;
     61     *flag  = nullptr;
     62 
     63     EventFlag* evFlag = new (std::nothrow) EventFlag(fwAddr, &status);
     64     if (evFlag != nullptr) {
     65         if (status == NO_ERROR) {
     66             *flag = evFlag;
     67         } else {
     68             delete evFlag;
     69         }
     70     }
     71 
     72     return status;
     73 }
     74 
     75 /*
     76  * mmap memory for the futex word
     77  */
     78 EventFlag::EventFlag(int fd, off_t offset, status_t* status) {
     79     mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL,
     80                                                           sizeof(std::atomic<uint32_t>),
     81                                                           PROT_READ | PROT_WRITE,
     82                                                           MAP_SHARED, fd, offset));
     83     mEfWordNeedsUnmapping = true;
     84     if (mEfWordPtr != MAP_FAILED) {
     85         *status = NO_ERROR;
     86     } else {
     87         *status = -errno;
     88         ALOGE("Attempt to mmap event flag word failed: %s\n", strerror(errno));
     89     }
     90 }
     91 
     92 /*
     93  * Use this constructor if we already know where the futex word for
     94  * the EventFlag group lives.
     95  */
     96 EventFlag::EventFlag(std::atomic<uint32_t>* fwAddr, status_t* status) {
     97     *status = NO_ERROR;
     98     if (fwAddr == nullptr) {
     99         *status = BAD_VALUE;
    100     } else {
    101         mEfWordPtr = fwAddr;
    102     }
    103 }
    104 
    105 /*
    106  * Set the specified bits of the futex word here and wake up any
    107  * thread waiting on any of the bits.
    108  */
    109 status_t EventFlag::wake(uint32_t bitmask) {
    110     /*
    111      * Return early if there are no set bits in bitmask.
    112      */
    113     if (bitmask == 0) {
    114         return NO_ERROR;
    115     }
    116 
    117     status_t status = NO_ERROR;
    118     uint32_t old = std::atomic_fetch_or(mEfWordPtr, bitmask);
    119     /*
    120      * No need to call FUTEX_WAKE_BITSET if there were deferred wakes
    121      * already available for all set bits from bitmask.
    122      */
    123     if ((~old & bitmask) != 0) {
    124         int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET,
    125                           INT_MAX, NULL, NULL, bitmask);
    126         if (ret == -1) {
    127             status = -errno;
    128             ALOGE("Error in event flag wake attempt: %s\n", strerror(errno));
    129         }
    130     }
    131     return status;
    132 }
    133 
    134 /*
    135  * Wait for any of the bits in the bitmask to be set
    136  * and return which bits caused the return.
    137  */
    138 status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
    139     /*
    140      * Return early if there are no set bits in bitmask.
    141      */
    142     if (bitmask == 0 || efState == nullptr) {
    143         return BAD_VALUE;
    144     }
    145 
    146     status_t status = NO_ERROR;
    147     uint32_t old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
    148     uint32_t setBits = old & bitmask;
    149     /*
    150      * If there was a deferred wake available, no need to call FUTEX_WAIT_BITSET.
    151      */
    152     if (setBits != 0) {
    153         *efState = setBits;
    154         return status;
    155     }
    156 
    157     uint32_t efWord = old & ~bitmask;
    158     /*
    159      * The syscall will put the thread to sleep only
    160      * if the futex word still contains the expected
    161      * value i.e. efWord. If the futex word contents have
    162      * changed, it fails with the error EAGAIN; If a timeout
    163      * is specified and exceeded the syscall fails with ETIMEDOUT.
    164      */
    165     int ret = 0;
    166     if (timeoutNanoSeconds) {
    167         struct timespec waitTimeAbsolute;
    168         addNanosecondsToCurrentTime(timeoutNanoSeconds, &waitTimeAbsolute);
    169 
    170         ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET,
    171                       efWord, &waitTimeAbsolute, NULL, bitmask);
    172     } else {
    173         ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET, efWord, NULL, NULL, bitmask);
    174     }
    175     if (ret == -1) {
    176         status = -errno;
    177         if (status != -EAGAIN && status != -ETIMEDOUT) {
    178             ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno));
    179         }
    180         *efState = 0;
    181     } else {
    182         old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
    183         *efState = old & bitmask;
    184 
    185         if (*efState == 0) {
    186             /* Return -EINTR for a spurious wakeup */
    187             status = -EINTR;
    188         }
    189     }
    190     return status;
    191 }
    192 
    193 /*
    194  * Wait for any of the bits in the bitmask to be set
    195  * and return which bits caused the return. If 'retry'
    196  * is true, wait again on a spurious wake-up.
    197  */
    198 status_t EventFlag::wait(uint32_t bitmask,
    199                          uint32_t* efState,
    200                          int64_t timeoutNanoSeconds,
    201                          bool retry) {
    202     if (!retry) {
    203         return waitHelper(bitmask, efState, timeoutNanoSeconds);
    204     }
    205 
    206     bool shouldTimeOut = timeoutNanoSeconds != 0;
    207     int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
    208     status_t status;
    209     while (true) {
    210         if (shouldTimeOut) {
    211             int64_t currentTimeNs = android::elapsedRealtimeNano();
    212             /*
    213              * Decrement TimeOutNanos to account for the time taken to complete the last
    214              * iteration of the while loop.
    215              */
    216             timeoutNanoSeconds -= currentTimeNs - prevTimeNs;
    217             prevTimeNs = currentTimeNs;
    218             if (timeoutNanoSeconds <= 0) {
    219                 status = -ETIMEDOUT;
    220                 *efState = 0;
    221                 break;
    222             }
    223         }
    224 
    225         status = waitHelper(bitmask, efState, timeoutNanoSeconds);
    226         if ((status != -EAGAIN) && (status != -EINTR)) {
    227             break;
    228         }
    229     }
    230     return status;
    231 }
    232 
    233 status_t EventFlag::unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr,
    234                                        bool* efWordNeedsUnmapping) {
    235     status_t status = NO_ERROR;
    236     if (*efWordNeedsUnmapping) {
    237         int ret = munmap(efWordPtr, sizeof(std::atomic<uint32_t>));
    238         if (ret != 0) {
    239             status = -errno;
    240             ALOGE("Error in deleting event flag group: %s\n", strerror(errno));
    241         }
    242         *efWordNeedsUnmapping = false;
    243     }
    244     return status;
    245 }
    246 
    247 status_t EventFlag::deleteEventFlag(EventFlag** evFlag) {
    248     if (evFlag == nullptr || *evFlag == nullptr) {
    249         return BAD_VALUE;
    250     }
    251 
    252     status_t status = unmapEventFlagWord((*evFlag)->mEfWordPtr,
    253                                          &(*evFlag)->mEfWordNeedsUnmapping);
    254     delete *evFlag;
    255     *evFlag = nullptr;
    256 
    257     return status;
    258 }
    259 
    260 void EventFlag::addNanosecondsToCurrentTime(int64_t nanoSeconds, struct timespec* waitTime) {
    261     static constexpr int64_t kNanosPerSecond = 1000000000;
    262 
    263     clock_gettime(CLOCK_MONOTONIC, waitTime);
    264     waitTime->tv_sec += nanoSeconds / kNanosPerSecond;
    265     waitTime->tv_nsec += nanoSeconds % kNanosPerSecond;
    266 
    267     if (waitTime->tv_nsec >= kNanosPerSecond) {
    268         waitTime->tv_sec++;
    269         waitTime->tv_nsec -= kNanosPerSecond;
    270     }
    271 }
    272 
    273 EventFlag::~EventFlag() {
    274     unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
    275 }
    276 
    277 }  // namespace hardware
    278 }  // namespace android
    279