Home | History | Annotate | Download | only in libcutils
      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. */
     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             LOGI("read() interrupted.");
     52         } else {
     53             LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
     54         }
     55     }
     56 }
     57 
     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 
     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 
     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             LOGI("read() interrupted.");
     81         } else {
     82             LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
     83         }
     84     }
     85 }
     86 
     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     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 
    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  */
    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  */
    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             	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  */
    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 		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  */
    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 
    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         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