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 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) { 51 LOGI("read() interrupted."); 52 } 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) { 80 LOGI("read() interrupted."); 81 } 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 99 LOGD("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 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)) { 172 LOGD("Selecting fd %d for writing...", selectableFd->fd); 173 inSet = true; 174 } 175 if (maybeAdd(selectableFd, selectableFd->onReadable, readFds)) { 176 LOGD("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 */ 201static 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 LOGD("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 */ 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 241 LOGD("Entering select()."); 242 243 // Select file descriptors. 244 int result = select(selector->maxFd + 1, &selector->readFds, 245 &selector->writeFds, &selector->exceptFds, NULL); 246 247 LOGD("Exiting select()."); 248 249 setInSelect(selector, false); 250 251 if (result == -1) { 252 // Abort on everything except EINTR. 253 if (errno == EINTR) { 254 LOGI("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