blob: 164e40c0625009ac1e73438c139694c69e5728ca [file] [log] [blame]
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -08001/*
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 <utils/Log.h>
24#include <new>
25
26namespace android {
27namespace hardware {
28
29status_t EventFlag::createEventFlag(int fd, off_t offset, EventFlag** flag) {
30 if (flag == nullptr) {
31 return BAD_VALUE;
32 }
33
34 status_t status = NO_MEMORY;
35 *flag = nullptr;
36
37 EventFlag* evFlag = new (std::nothrow) EventFlag(fd, offset, &status);
38 if (evFlag != nullptr) {
39 if (status == NO_ERROR) {
40 *flag = evFlag;
41 } else {
42 delete evFlag;
43 }
44 }
45
46 return status;
47}
48
49status_t EventFlag::createEventFlag(std::atomic<uint32_t>* fwAddr,
50 EventFlag** flag) {
51 if (flag == nullptr) {
52 return BAD_VALUE;
53 }
54
55 status_t status = NO_MEMORY;
56 *flag = nullptr;
57
58 EventFlag* evFlag = new (std::nothrow) EventFlag(fwAddr, &status);
59 if (evFlag != nullptr) {
60 if (status == NO_ERROR) {
61 *flag = evFlag;
62 } else {
63 delete evFlag;
64 }
65 }
66
67 return status;
68}
69
70/*
71 * mmap memory for the futex word
72 */
73EventFlag::EventFlag(int fd, off_t offset, status_t* status) {
74 mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL,
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -080075 sizeof(std::atomic<uint32_t>),
76 PROT_READ | PROT_WRITE,
77 MAP_SHARED, fd, offset));
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080078 mEfWordNeedsUnmapping = true;
79 if (mEfWordPtr != MAP_FAILED) {
80 *status = NO_ERROR;
81 } else {
82 *status = -errno;
83 ALOGE("Attempt to mmap event flag word failed: %s\n", strerror(errno));
84 }
85}
86
87/*
88 * Use this constructor if we already know where the futex word for
89 * the EventFlag group lives.
90 */
91EventFlag::EventFlag(std::atomic<uint32_t>* fwAddr, status_t* status) {
92 *status = NO_ERROR;
93 if (fwAddr == nullptr) {
94 *status = BAD_VALUE;
95 } else {
96 mEfWordPtr = fwAddr;
97 }
98}
99
100/*
101 * Set the specified bits of the futex word here and wake up any
102 * thread waiting on any of the bits.
103 */
104status_t EventFlag::wake(uint32_t bitmask) {
105 /*
106 * Return early if there are no set bits in bitmask.
107 */
108 if (bitmask == 0) {
109 return NO_ERROR;
110 }
111
112 status_t status = NO_ERROR;
113 uint32_t old = std::atomic_fetch_or(mEfWordPtr, bitmask);
114 /*
Hridya Valsarajuaf605d42017-01-19 12:12:29 -0800115 * No need to call FUTEX_WAKE_BITSET if there were deferred wakes
116 * already available for all set bits from bitmask.
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800117 */
118 if ((~old & bitmask) != 0) {
119 int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET,
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -0800120 INT_MAX, NULL, NULL, bitmask);
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800121 if (ret == -1) {
122 status = -errno;
123 ALOGE("Error in event flag wake attempt: %s\n", strerror(errno));
124 }
125 }
126 return status;
127}
128
129/*
130 * Wait for any of the bits in the bitmask to be set
131 * and return which bits caused the return.
132 */
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -0800133status_t EventFlag::wait(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800134 /*
135 * Return early if there are no set bits in bitmask.
136 */
137 if (bitmask == 0 || efState == nullptr) {
138 return BAD_VALUE;
139 }
140
141 status_t status = NO_ERROR;
142 uint32_t old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
143 uint32_t setBits = old & bitmask;
144 /*
145 * If there was a deferred wake available, no need to call FUTEX_WAIT_BITSET.
146 */
147 if (setBits != 0) {
148 *efState = setBits;
149 return status;
150 }
151
152 uint32_t efWord = old & ~bitmask;
153 /*
154 * The syscall will put the thread to sleep only
155 * if the futex word still contains the expected
156 * value i.e. efWord. If the futex word contents have
Andreas Huber3c2dcb12017-01-11 12:40:22 -0800157 * changed, it fails with the error EAGAIN; If a timeout
158 * is specified and exceeded the syscall fails with ETIMEDOUT.
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800159 */
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -0800160 int ret = 0;
161 if (timeoutNanoSeconds) {
162 struct timespec waitTimeAbsolute;
163 addNanosecondsToCurrentTime(timeoutNanoSeconds, &waitTimeAbsolute);
164
165 ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET,
166 efWord, &waitTimeAbsolute, NULL, bitmask);
167 } else {
168 ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET, efWord, NULL, NULL, bitmask);
169 }
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800170 if (ret == -1) {
171 status = -errno;
Andreas Huber3c2dcb12017-01-11 12:40:22 -0800172 if (status != -EAGAIN && status != -ETIMEDOUT) {
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800173 ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno));
174 }
175 *efState = 0;
176 } else {
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800177 old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
178 *efState = old & bitmask;
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800179 }
180 return status;
181}
182
183status_t EventFlag::unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr,
184 bool* efWordNeedsUnmapping) {
185 status_t status = NO_ERROR;
186 if (*efWordNeedsUnmapping) {
187 int ret = munmap(efWordPtr, sizeof(std::atomic<uint32_t>));
188 if (ret != 0) {
189 status = -errno;
190 ALOGE("Error in deleting event flag group: %s\n", strerror(errno));
191 }
192 *efWordNeedsUnmapping = false;
193 }
194 return status;
195}
196
197status_t EventFlag::deleteEventFlag(EventFlag** evFlag) {
198 if (evFlag == nullptr || *evFlag == nullptr) {
199 return BAD_VALUE;
200 }
201
202 status_t status = unmapEventFlagWord((*evFlag)->mEfWordPtr,
203 &(*evFlag)->mEfWordNeedsUnmapping);
204 delete *evFlag;
205 *evFlag = nullptr;
206
207 return status;
208}
209
Hridya Valsaraju10f59dc2016-12-20 12:50:44 -0800210void EventFlag::addNanosecondsToCurrentTime(int64_t nanoSeconds, struct timespec* waitTime) {
211 static constexpr int64_t kNanosPerSecond = 1000000000;
212
213 clock_gettime(CLOCK_MONOTONIC, waitTime);
214 waitTime->tv_sec += nanoSeconds / kNanosPerSecond;
215 waitTime->tv_nsec += nanoSeconds % kNanosPerSecond;
216
217 if (waitTime->tv_nsec >= kNanosPerSecond) {
218 waitTime->tv_sec++;
219 waitTime->tv_nsec -= kNanosPerSecond;
220 }
221}
222
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800223EventFlag::~EventFlag() {
224 unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
225}
226
227} // namespace hardware
228} // namespace android