• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 namespace android {
29 namespace hardware {
30 
createEventFlag(int fd,off_t offset,EventFlag ** flag)31 status_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 
createEventFlag(std::atomic<uint32_t> * fwAddr,EventFlag ** flag)51 status_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  */
EventFlag(int fd,off_t offset,status_t * status)75 EventFlag::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  */
EventFlag(std::atomic<uint32_t> * fwAddr,status_t * status)93 EventFlag::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  */
wake(uint32_t bitmask)106 status_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  */
waitHelper(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds)135 status_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  */
wait(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds,bool retry)195 status_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 
unmapEventFlagWord(std::atomic<uint32_t> * efWordPtr,bool * efWordNeedsUnmapping)230 status_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 
deleteEventFlag(EventFlag ** evFlag)244 status_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 
addNanosecondsToCurrentTime(int64_t nanoSeconds,struct timespec * waitTime)257 void 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 
~EventFlag()270 EventFlag::~EventFlag() {
271     unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
272 }
273 
274 }  // namespace hardware
275 }  // namespace android
276