diff options
Diffstat (limited to 'libs/utils/PollLoop.cpp')
| -rw-r--r-- | libs/utils/PollLoop.cpp | 377 | 
1 files changed, 377 insertions, 0 deletions
| diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp new file mode 100644 index 000000000000..fe76cd08cef7 --- /dev/null +++ b/libs/utils/PollLoop.cpp @@ -0,0 +1,377 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A select loop implementation. +// +#define LOG_TAG "PollLoop" + +//#define LOG_NDEBUG 0 + +// Debugs poll and wake interactions. +#define DEBUG_POLL_AND_WAKE 0 + +// Debugs callback registration and invocation. +#define DEBUG_CALLBACKS 0 + +#include <cutils/log.h> +#include <utils/PollLoop.h> + +#include <unistd.h> +#include <fcntl.h> + +namespace android { + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + +PollLoop::PollLoop(bool allowNonCallbacks) : +        mAllowNonCallbacks(allowNonCallbacks), mPolling(false), +        mWaiters(0), mPendingFdsPos(0) { +    openWakePipe(); +} + +PollLoop::~PollLoop() { +    closeWakePipe(); +} + +void PollLoop::threadDestructor(void *st) { +    PollLoop* const self = static_cast<PollLoop*>(st); +    if (self != NULL) { +        self->decStrong((void*)threadDestructor); +    } +} + +void PollLoop::setForThread(const sp<PollLoop>& pollLoop) { +    sp<PollLoop> old = getForThread(); +     +    if (pollLoop != NULL) { +        pollLoop->incStrong((void*)threadDestructor); +    } +     +    pthread_setspecific(gTLS, pollLoop.get()); +     +    if (old != NULL) { +        old->decStrong((void*)threadDestructor); +    } +} +     +sp<PollLoop> PollLoop::getForThread() { +    if (!gHaveTLS) { +        pthread_mutex_lock(&gTLSMutex); +        if (pthread_key_create(&gTLS, threadDestructor) != 0) { +            pthread_mutex_unlock(&gTLSMutex); +            return NULL; +        } +        gHaveTLS = true; +        pthread_mutex_unlock(&gTLSMutex); +    } +     +    return (PollLoop*)pthread_getspecific(gTLS); +} + +void PollLoop::openWakePipe() { +    int wakeFds[2]; +    int result = pipe(wakeFds); +    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno); + +    mWakeReadPipeFd = wakeFds[0]; +    mWakeWritePipeFd = wakeFds[1]; + +    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); +    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d", +            errno); + +    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); +    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d", +            errno); + +    // Add the wake pipe to the head of the request list with a null callback. +    struct pollfd requestedFd; +    requestedFd.fd = mWakeReadPipeFd; +    requestedFd.events = POLLIN; +    mRequestedFds.insertAt(requestedFd, 0); + +    RequestedCallback requestedCallback; +    requestedCallback.callback = NULL; +    requestedCallback.looperCallback = NULL; +    requestedCallback.ident = 0; +    requestedCallback.data = NULL; +    mRequestedCallbacks.insertAt(requestedCallback, 0); +} + +void PollLoop::closeWakePipe() { +    close(mWakeReadPipeFd); +    close(mWakeWritePipeFd); + +    // Note: We don't need to remove the poll structure or callback entry because this +    //       method is currently only called by the destructor. +} + +int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { +    // If there are still pending fds from the last call, dispatch those +    // first, to avoid an earlier fd from starving later ones. +    const size_t pendingFdsCount = mPendingFds.size(); +    if (mPendingFdsPos < pendingFdsCount) { +        const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos); +        mPendingFdsPos++; +        if (outEvents != NULL) *outEvents = pending.events; +        if (outData != NULL) *outData = pending.data; +        return pending.ident; +    } + +    // Wait for wakeAndLock() waiters to run then set mPolling to true. +    mLock.lock(); +    while (mWaiters != 0) { +        mResume.wait(mLock); +    } +    mPolling = true; +    mLock.unlock(); + +    // Poll. +    int32_t result; +    size_t requestedCount = mRequestedFds.size(); + +#if DEBUG_POLL_AND_WAKE +    LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount); +    for (size_t i = 0; i < requestedCount; i++) { +        LOGD("  fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events); +    } +#endif + +    int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); + +    if (respondedCount == 0) { +        // Timeout +#if DEBUG_POLL_AND_WAKE +        LOGD("%p ~ pollOnce - timeout", this); +#endif +        result = POLL_TIMEOUT; +        goto Done; +    } + +    if (respondedCount < 0) { +        // Error +#if DEBUG_POLL_AND_WAKE +        LOGD("%p ~ pollOnce - error, errno=%d", this, errno); +#endif +        if (errno != EINTR) { +            LOGW("Poll failed with an unexpected error, errno=%d", errno); +        } +        result = POLL_ERROR; +        goto Done; +    } + +#if DEBUG_POLL_AND_WAKE +    LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount); +    for (size_t i = 0; i < requestedCount; i++) { +        LOGD("  fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events, +                mRequestedFds[i].revents); +    } +#endif + +    // Process the poll results. +    mPendingCallbacks.clear(); +    mPendingFds.clear(); +    mPendingFdsPos = 0; +    if (outEvents != NULL) *outEvents = 0; +    if (outData != NULL) *outData = NULL; +     +    result = POLL_CALLBACK; +    for (size_t i = 0; i < requestedCount; i++) { +        const struct pollfd& requestedFd = mRequestedFds.itemAt(i); + +        short revents = requestedFd.revents; +        if (revents) { +            const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); +            PendingCallback pending; +            pending.fd = requestedFd.fd; +            pending.ident = requestedCallback.ident; +            pending.events = revents; +            pending.callback = requestedCallback.callback; +            pending.looperCallback = requestedCallback.looperCallback; +            pending.data = requestedCallback.data; + +            if (pending.callback || pending.looperCallback) { +                mPendingCallbacks.push(pending); +            } else if (pending.fd != mWakeReadPipeFd) { +                if (result == POLL_CALLBACK) { +                    result = pending.ident; +                    if (outEvents != NULL) *outEvents = pending.events; +                    if (outData != NULL) *outData = pending.data; +                } else { +                    mPendingFds.push(pending); +                } +            } else { +#if DEBUG_POLL_AND_WAKE +                LOGD("%p ~ pollOnce - awoken", this); +#endif +                char buffer[16]; +                ssize_t nRead; +                do { +                    nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); +                } while (nRead == sizeof(buffer)); +            } + +            respondedCount -= 1; +            if (respondedCount == 0) { +                break; +            } +        } +    } + +Done: +    // Set mPolling to false and wake up the wakeAndLock() waiters. +    mLock.lock(); +    mPolling = false; +    if (mWaiters != 0) { +        mAwake.broadcast(); +    } +    mLock.unlock(); + +    if (result == POLL_CALLBACK || result >= 0) { +        size_t pendingCount = mPendingCallbacks.size(); +        for (size_t i = 0; i < pendingCount; i++) { +            const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS +            LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd); +#endif + +            bool keep = true; +            if (pendingCallback.callback != NULL) { +                keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events, +                        pendingCallback.data); +            } else { +                keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events, +                        pendingCallback.data) != 0; +            } +            if (! keep) { +                removeCallback(pendingCallback.fd); +            } +        } +    } + +#if DEBUG_POLL_AND_WAKE +    LOGD("%p ~ pollOnce - done", this); +#endif +    return result; +} + +void PollLoop::wake() { +#if DEBUG_POLL_AND_WAKE +    LOGD("%p ~ wake", this); +#endif + +    ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); +    if (nWrite != 1) { +        if (errno != EAGAIN) { +            LOGW("Could not write wake signal, errno=%d", errno); +        } +    } +} + +bool PollLoop::getAllowNonCallbacks() const { +    return mAllowNonCallbacks; +} + +void PollLoop::setCallback(int fd, int ident, int events, Callback callback, void* data) { +    setCallbackCommon(fd, ident, events, callback, NULL, data); +} + +void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { +    setCallbackCommon(fd, POLL_CALLBACK, events, callback, NULL, data); +} + +void PollLoop::setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, +        void* data) { +    setCallbackCommon(fd, ident, events, NULL, callback, data); +} + +void PollLoop::setCallbackCommon(int fd, int ident, int events, Callback callback, +        ALooper_callbackFunc* looperCallback, void* data) { + +#if DEBUG_CALLBACKS +    LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); +#endif + +    if (! events) { +        LOGE("Invalid attempt to set a callback with no selected poll events."); +        removeCallback(fd); +        return; +    } + +    if (! callback && ! looperCallback && ! mAllowNonCallbacks) { +        LOGE("Invalid attempt to set NULL callback but not allowed."); +        removeCallback(fd); +        return; +    } +     +    wakeAndLock(); + +    struct pollfd requestedFd; +    requestedFd.fd = fd; +    requestedFd.events = events; + +    RequestedCallback requestedCallback; +    requestedCallback.callback = callback; +    requestedCallback.looperCallback = looperCallback; +    requestedCallback.ident = ident; +    requestedCallback.data = data; + +    ssize_t index = getRequestIndexLocked(fd); +    if (index < 0) { +        mRequestedFds.push(requestedFd); +        mRequestedCallbacks.push(requestedCallback); +    } else { +        mRequestedFds.replaceAt(requestedFd, size_t(index)); +        mRequestedCallbacks.replaceAt(requestedCallback, size_t(index)); +    } + +    mLock.unlock(); +} + +bool PollLoop::removeCallback(int fd) { +#if DEBUG_CALLBACKS +    LOGD("%p ~ removeCallback - fd=%d", this, fd); +#endif + +    wakeAndLock(); + +    ssize_t index = getRequestIndexLocked(fd); +    if (index >= 0) { +        mRequestedFds.removeAt(size_t(index)); +        mRequestedCallbacks.removeAt(size_t(index)); +    } + +    mLock.unlock(); +    return index >= 0; +} + +ssize_t PollLoop::getRequestIndexLocked(int fd) { +    size_t requestCount = mRequestedFds.size(); + +    for (size_t i = 0; i < requestCount; i++) { +        if (mRequestedFds.itemAt(i).fd == fd) { +            return i; +        } +    } + +    return -1; +} + +void PollLoop::wakeAndLock() { +    mLock.lock(); + +    mWaiters += 1; +    while (mPolling) { +        wake(); +        mAwake.wait(mLock); +    } + +    mWaiters -= 1; +    if (mWaiters == 0) { +        mResume.signal(); +    } +} + +} // namespace android | 
