1 /*
2 * Copyright (C) 2007 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 "selector"
18
19 #include <assert.h>
20 #include <errno.h>
21 #include <pthread.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <cutils/array.h>
28 #include <cutils/selector.h>
29
30 #include "loghack.h"
31
32 struct Selector {
33 Array* selectableFds;
34 bool looping;
35 fd_set readFds;
36 fd_set writeFds;
37 fd_set exceptFds;
38 int maxFd;
39 int wakeupPipe[2];
40 SelectableFd* wakeupFd;
41
42 bool inSelect;
43 pthread_mutex_t inSelectLock;
44 };
45
46 /** Reads and ignores wake up data. */
eatWakeupData(SelectableFd * wakeupFd)47 static void eatWakeupData(SelectableFd* wakeupFd) {
48 static char garbage[64];
49 if (read(wakeupFd->fd, garbage, sizeof(garbage)) < 0) {
50 if (errno == EINTR) {
51 ALOGI("read() interrupted.");
52 } else {
53 LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
54 }
55 }
56 }
57
setInSelect(Selector * selector,bool inSelect)58 static void setInSelect(Selector* selector, bool inSelect) {
59 pthread_mutex_lock(&selector->inSelectLock);
60 selector->inSelect = inSelect;
61 pthread_mutex_unlock(&selector->inSelectLock);
62 }
63
isInSelect(Selector * selector)64 static bool isInSelect(Selector* selector) {
65 pthread_mutex_lock(&selector->inSelectLock);
66 bool inSelect = selector->inSelect;
67 pthread_mutex_unlock(&selector->inSelectLock);
68 return inSelect;
69 }
70
selectorWakeUp(Selector * selector)71 void selectorWakeUp(Selector* selector) {
72 if (!isInSelect(selector)) {
73 // We only need to write wake-up data if we're blocked in select().
74 return;
75 }
76
77 static char garbage[1];
78 if (write(selector->wakeupPipe[1], garbage, sizeof(garbage)) < 0) {
79 if (errno == EINTR) {
80 ALOGI("read() interrupted.");
81 } else {
82 LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
83 }
84 }
85 }
86
selectorCreate(void)87 Selector* selectorCreate(void) {
88 Selector* selector = calloc(1, sizeof(Selector));
89 if (selector == NULL) {
90 LOG_ALWAYS_FATAL("malloc() error.");
91 }
92 selector->selectableFds = arrayCreate();
93
94 // Set up wake-up pipe.
95 if (pipe(selector->wakeupPipe) < 0) {
96 LOG_ALWAYS_FATAL("pipe() error: %s", strerror(errno));
97 }
98
99 ALOGD("Wakeup fd: %d", selector->wakeupPipe[0]);
100
101 SelectableFd* wakeupFd = selectorAdd(selector, selector->wakeupPipe[0]);
102 if (wakeupFd == NULL) {
103 LOG_ALWAYS_FATAL("malloc() error.");
104 }
105 wakeupFd->onReadable = &eatWakeupData;
106
107 pthread_mutex_init(&selector->inSelectLock, NULL);
108
109 return selector;
110 }
111
selectorAdd(Selector * selector,int fd)112 SelectableFd* selectorAdd(Selector* selector, int fd) {
113 assert(selector != NULL);
114
115 SelectableFd* selectableFd = calloc(1, sizeof(SelectableFd));
116 if (selectableFd != NULL) {
117 selectableFd->selector = selector;
118 selectableFd->fd = fd;
119
120 arrayAdd(selector->selectableFds, selectableFd);
121 }
122
123 return selectableFd;
124 }
125
126 /**
127 * Adds an fd to the given set if the callback is non-null. Returns true
128 * if the fd was added.
129 */
maybeAdd(SelectableFd * selectableFd,void (* callback)(SelectableFd *),fd_set * fdSet)130 static inline bool maybeAdd(SelectableFd* selectableFd,
131 void (*callback)(SelectableFd*), fd_set* fdSet) {
132 if (callback != NULL) {
133 FD_SET(selectableFd->fd, fdSet);
134 return true;
135 }
136 return false;
137 }
138
139 /**
140 * Removes stale file descriptors and initializes file descriptor sets.
141 */
prepareForSelect(Selector * selector)142 static void prepareForSelect(Selector* selector) {
143 fd_set* exceptFds = &selector->exceptFds;
144 fd_set* readFds = &selector->readFds;
145 fd_set* writeFds = &selector->writeFds;
146
147 FD_ZERO(exceptFds);
148 FD_ZERO(readFds);
149 FD_ZERO(writeFds);
150
151 Array* selectableFds = selector->selectableFds;
152 int i = 0;
153 selector->maxFd = 0;
154 int size = arraySize(selectableFds);
155 while (i < size) {
156 SelectableFd* selectableFd = arrayGet(selectableFds, i);
157 if (selectableFd->remove) {
158 // This descriptor should be removed.
159 arrayRemove(selectableFds, i);
160 size--;
161 if (selectableFd->onRemove != NULL) {
162 selectableFd->onRemove(selectableFd);
163 }
164 free(selectableFd);
165 } else {
166 if (selectableFd->beforeSelect != NULL) {
167 selectableFd->beforeSelect(selectableFd);
168 }
169
170 bool inSet = false;
171 if (maybeAdd(selectableFd, selectableFd->onExcept, exceptFds)) {
172 ALOGD("Selecting fd %d for writing...", selectableFd->fd);
173 inSet = true;
174 }
175 if (maybeAdd(selectableFd, selectableFd->onReadable, readFds)) {
176 ALOGD("Selecting fd %d for reading...", selectableFd->fd);
177 inSet = true;
178 }
179 if (maybeAdd(selectableFd, selectableFd->onWritable, writeFds)) {
180 inSet = true;
181 }
182
183 if (inSet) {
184 // If the fd is in a set, check it against max.
185 int fd = selectableFd->fd;
186 if (fd > selector->maxFd) {
187 selector->maxFd = fd;
188 }
189 }
190
191 // Move to next descriptor.
192 i++;
193 }
194 }
195 }
196
197 /**
198 * Invokes a callback if the callback is non-null and the fd is in the given
199 * set.
200 */
maybeInvoke(SelectableFd * selectableFd,void (* callback)(SelectableFd *),fd_set * fdSet)201 static inline void maybeInvoke(SelectableFd* selectableFd,
202 void (*callback)(SelectableFd*), fd_set* fdSet) {
203 if (callback != NULL && !selectableFd->remove &&
204 FD_ISSET(selectableFd->fd, fdSet)) {
205 ALOGD("Selected fd %d.", selectableFd->fd);
206 callback(selectableFd);
207 }
208 }
209
210 /**
211 * Notifies user if file descriptors are readable or writable, or if
212 * out-of-band data is present.
213 */
fireEvents(Selector * selector)214 static void fireEvents(Selector* selector) {
215 Array* selectableFds = selector->selectableFds;
216 int size = arraySize(selectableFds);
217 int i;
218 for (i = 0; i < size; i++) {
219 SelectableFd* selectableFd = arrayGet(selectableFds, i);
220 maybeInvoke(selectableFd, selectableFd->onExcept,
221 &selector->exceptFds);
222 maybeInvoke(selectableFd, selectableFd->onReadable,
223 &selector->readFds);
224 maybeInvoke(selectableFd, selectableFd->onWritable,
225 &selector->writeFds);
226 }
227 }
228
selectorLoop(Selector * selector)229 void selectorLoop(Selector* selector) {
230 // Make sure we're not already looping.
231 if (selector->looping) {
232 LOG_ALWAYS_FATAL("Already looping.");
233 }
234 selector->looping = true;
235
236 while (true) {
237 setInSelect(selector, true);
238
239 prepareForSelect(selector);
240
241 ALOGD("Entering select().");
242
243 // Select file descriptors.
244 int result = select(selector->maxFd + 1, &selector->readFds,
245 &selector->writeFds, &selector->exceptFds, NULL);
246
247 ALOGD("Exiting select().");
248
249 setInSelect(selector, false);
250
251 if (result == -1) {
252 // Abort on everything except EINTR.
253 if (errno == EINTR) {
254 ALOGI("select() interrupted.");
255 } else {
256 LOG_ALWAYS_FATAL("select() error: %s",
257 strerror(errno));
258 }
259 } else if (result > 0) {
260 fireEvents(selector);
261 }
262 }
263 }
264