• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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