blob: 3776bbbc504b43e9982c76b9c2879a0be320d1e8 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
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
32struct 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. */
47static void eatWakeupData(SelectableFd* wakeupFd) {
48 static char garbage[64];
49 if (read(wakeupFd->fd, garbage, sizeof(garbage)) < 0) {
50 if (errno == EINTR) {
Steve Block4163b452012-01-04 19:19:03 +000051 ALOGI("read() interrupted.");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080052 } else {
53 LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
54 }
55 }
56}
57
58static void setInSelect(Selector* selector, bool inSelect) {
59 pthread_mutex_lock(&selector->inSelectLock);
60 selector->inSelect = inSelect;
61 pthread_mutex_unlock(&selector->inSelectLock);
62}
63
64static 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
71void 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) {
Steve Block4163b452012-01-04 19:19:03 +000080 ALOGI("read() interrupted.");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080081 } else {
82 LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
83 }
84 }
85}
86
87Selector* 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
Steve Block9786ec42011-12-20 16:07:45 +000099 ALOGD("Wakeup fd: %d", selector->wakeupPipe[0]);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800100
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
112SelectableFd* 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 */
130static 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 */
142static 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)) {
Steve Block9786ec42011-12-20 16:07:45 +0000172 ALOGD("Selecting fd %d for writing...", selectableFd->fd);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800173 inSet = true;
174 }
175 if (maybeAdd(selectableFd, selectableFd->onReadable, readFds)) {
Steve Block9786ec42011-12-20 16:07:45 +0000176 ALOGD("Selecting fd %d for reading...", selectableFd->fd);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800177 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 */
201static inline void maybeInvoke(SelectableFd* selectableFd,
202 void (*callback)(SelectableFd*), fd_set* fdSet) {
Steve Block9786ec42011-12-20 16:07:45 +0000203 if (callback != NULL && !selectableFd->remove &&
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800204 FD_ISSET(selectableFd->fd, fdSet)) {
Steve Block9786ec42011-12-20 16:07:45 +0000205 ALOGD("Selected fd %d.", selectableFd->fd);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800206 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 */
214static 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
229void 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
Steve Block9786ec42011-12-20 16:07:45 +0000241 ALOGD("Entering select().");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800242
243 // Select file descriptors.
244 int result = select(selector->maxFd + 1, &selector->readFds,
245 &selector->writeFds, &selector->exceptFds, NULL);
246
Steve Block9786ec42011-12-20 16:07:45 +0000247 ALOGD("Exiting select().");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800248
249 setInSelect(selector, false);
250
251 if (result == -1) {
252 // Abort on everything except EINTR.
253 if (errno == EINTR) {
Steve Block4163b452012-01-04 19:19:03 +0000254 ALOGI("select() interrupted.");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800255 } else {
256 LOG_ALWAYS_FATAL("select() error: %s",
257 strerror(errno));
258 }
259 } else if (result > 0) {
260 fireEvents(selector);
261 }
262 }
263}