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