diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-09-15 12:32:29 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-09-15 12:32:29 -0700 |
commit | 49478536b6b8b28f33398393bed5e939085af7af (patch) | |
tree | a83f41bc6da9dd267005ad23b0b32c6f40f860d0 /libs/utils | |
parent | a5ab8ce602ecfd897805e185c89a6eff0c78aabe (diff) | |
parent | 14bc6b5d0677e5c454a67775c852f90389bb4567 (diff) | |
download | frameworks_base-49478536b6b8b28f33398393bed5e939085af7af.zip frameworks_base-49478536b6b8b28f33398393bed5e939085af7af.tar.gz frameworks_base-49478536b6b8b28f33398393bed5e939085af7af.tar.bz2 |
am 14bc6b5d: am 09340a4b: Merge "Replace epoll() with poll() and rename PollLoop to Looper." into gingerbread
Merge commit '14bc6b5d0677e5c454a67775c852f90389bb4567'
* commit '14bc6b5d0677e5c454a67775c852f90389bb4567':
Replace epoll() with poll() and rename PollLoop to Looper.
Diffstat (limited to 'libs/utils')
-rw-r--r-- | libs/utils/Android.mk | 2 | ||||
-rw-r--r-- | libs/utils/Looper.cpp | 368 | ||||
-rw-r--r-- | libs/utils/PollLoop.cpp | 377 | ||||
-rw-r--r-- | libs/utils/tests/Android.mk | 2 | ||||
-rw-r--r-- | libs/utils/tests/Looper_test.cpp | 433 | ||||
-rw-r--r-- | libs/utils/tests/PollLoop_test.cpp | 370 |
6 files changed, 803 insertions, 749 deletions
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 2e20268..eb75ed8 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -86,7 +86,7 @@ LOCAL_SRC_FILES:= \ $(commonSources) \ BackupData.cpp \ BackupHelpers.cpp \ - PollLoop.cpp + Looper.cpp ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp new file mode 100644 index 0000000..fd287da --- /dev/null +++ b/libs/utils/Looper.cpp @@ -0,0 +1,368 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A looper implementation based on epoll(). +// +#define LOG_TAG "Looper" + +//#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/Looper.h> +#include <utils/Timers.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/epoll.h> + + +namespace android { + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + +// Hint for number of file descriptors to be associated with the epoll instance. +static const int EPOLL_SIZE_HINT = 8; + +// Maximum number of file descriptors for which to retrieve poll events each iteration. +static const int EPOLL_MAX_EVENTS = 16; + +Looper::Looper(bool allowNonCallbacks) : + mAllowNonCallbacks(allowNonCallbacks), + mResponseIndex(0) { + mEpollFd = epoll_create(EPOLL_SIZE_HINT); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); + + 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); + + struct epoll_event eventItem; + eventItem.events = EPOLLIN; + eventItem.data.fd = mWakeReadPipeFd; + result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", + errno); +} + +Looper::~Looper() { + close(mWakeReadPipeFd); + close(mWakeWritePipeFd); + close(mEpollFd); +} + +void Looper::threadDestructor(void *st) { + Looper* const self = static_cast<Looper*>(st); + if (self != NULL) { + self->decStrong((void*)threadDestructor); + } +} + +void Looper::setForThread(const sp<Looper>& looper) { + sp<Looper> old = getForThread(); // also has side-effect of initializing TLS + + if (looper != NULL) { + looper->incStrong((void*)threadDestructor); + } + + pthread_setspecific(gTLS, looper.get()); + + if (old != NULL) { + old->decStrong((void*)threadDestructor); + } +} + +sp<Looper> Looper::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 (Looper*)pthread_getspecific(gTLS); +} + +sp<Looper> Looper::prepare(int opts) { + bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS; + sp<Looper> looper = Looper::getForThread(); + if (looper == NULL) { + looper = new Looper(allowNonCallbacks); + Looper::setForThread(looper); + } + if (looper->getAllowNonCallbacks() != allowNonCallbacks) { + LOGW("Looper already prepared for this thread with a different value for the " + "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); + } + return looper; +} + +bool Looper::getAllowNonCallbacks() const { + return mAllowNonCallbacks; +} + +int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + int result = 0; + for (;;) { + while (mResponseIndex < mResponses.size()) { + const Response& response = mResponses.itemAt(mResponseIndex++); + if (! response.request.callback) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - returning signalled identifier %d: " + "fd=%d, events=0x%x, data=%p", this, + response.request.ident, response.request.fd, + response.events, response.request.data); +#endif + if (outFd != NULL) *outFd = response.request.fd; + if (outEvents != NULL) *outEvents = response.events; + if (outData != NULL) *outData = response.request.data; + return response.request.ident; + } + } + + if (result != 0) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - returning result %d", this, result); +#endif + if (outFd != NULL) *outFd = 0; + if (outEvents != NULL) *outEvents = NULL; + if (outData != NULL) *outData = NULL; + return result; + } + + result = pollInner(timeoutMillis); + } +} + +int Looper::pollInner(int timeoutMillis) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); +#endif + struct epoll_event eventItems[EPOLL_MAX_EVENTS]; + int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); + if (eventCount < 0) { + if (errno != EINTR) { + LOGW("Poll failed with an unexpected error, errno=%d", errno); + } + return ALOOPER_POLL_ERROR; + } + + if (eventCount == 0) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - timeout", this); +#endif + return ALOOPER_POLL_TIMEOUT; + } + + int result = ALOOPER_POLL_WAKE; + mResponses.clear(); + mResponseIndex = 0; + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); +#endif + { // acquire lock + AutoMutex _l(mLock); + for (int i = 0; i < eventCount; i++) { + int fd = eventItems[i].data.fd; + uint32_t epollEvents = eventItems[i].events; + if (fd == mWakeReadPipeFd) { + if (epollEvents & EPOLLIN) { +#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)); + } else { + LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); + } + } else { + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex >= 0) { + int events = 0; + if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; + if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; + if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; + if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; + + Response response; + response.events = events; + response.request = mRequests.valueAt(requestIndex); + mResponses.push(response); + } else { + LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " + "no longer registered.", epollEvents, fd); + } + } + } + } + + for (size_t i = 0; i < mResponses.size(); i++) { + const Response& response = mResponses.itemAt(i); + if (response.request.callback) { +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this, + response.request.fd, response.events, response.request.data); +#endif + int callbackResult = response.request.callback( + response.request.fd, response.events, response.request.data); + if (callbackResult == 0) { + removeFd(response.request.fd); + } + + result = ALOOPER_POLL_CALLBACK; + } + } + return result; +} + +int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + if (timeoutMillis <= 0) { + int result; + do { + result = pollOnce(timeoutMillis, outFd, outEvents, outData); + } while (result == ALOOPER_POLL_CALLBACK); + return result; + } else { + nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC) + + milliseconds_to_nanoseconds(timeoutMillis); + + for (;;) { + int result = pollOnce(timeoutMillis, outFd, outEvents, outData); + if (result != ALOOPER_POLL_CALLBACK) { + return result; + } + + nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC); + if (timeoutNanos <= 0) { + return ALOOPER_POLL_TIMEOUT; + } + + timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL)); + } + } +} + +void Looper::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); + } + } +} + +int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { +#if DEBUG_CALLBACKS + LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, + events, callback, data); +#endif + + int epollEvents = 0; + if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; + if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; + if (events & ALOOPER_EVENT_ERROR) epollEvents |= EPOLLERR; + if (events & ALOOPER_EVENT_HANGUP) epollEvents |= EPOLLHUP; + + if (epollEvents == 0) { + LOGE("Invalid attempt to set a callback with no selected poll events."); + return -1; + } + + if (! callback) { + if (! mAllowNonCallbacks) { + LOGE("Invalid attempt to set NULL callback but not allowed for this looper."); + return -1; + } + + if (ident < 0) { + LOGE("Invalid attempt to set NULL callback with ident <= 0."); + return -1; + } + } + + { // acquire lock + AutoMutex _l(mLock); + + Request request; + request.fd = fd; + request.ident = ident; + request.callback = callback; + request.data = data; + + struct epoll_event eventItem; + eventItem.events = epollEvents; + eventItem.data.fd = fd; + + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex < 0) { + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); + if (epollResult < 0) { + LOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + mRequests.add(fd, request); + } else { + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); + if (epollResult < 0) { + LOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + mRequests.replaceValueAt(requestIndex, request); + } + } // release lock + return 1; +} + +int Looper::removeFd(int fd) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeFd - fd=%d", this, fd); +#endif + + { // acquire lock + AutoMutex _l(mLock); + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex < 0) { + return 0; + } + + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); + if (epollResult < 0) { + LOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + + mRequests.removeItemsAt(requestIndex); + } // request lock + return 1; +} + +} // namespace android diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp deleted file mode 100644 index fe76cd0..0000000 --- a/libs/utils/PollLoop.cpp +++ /dev/null @@ -1,377 +0,0 @@ -// -// 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 diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 725de9c..00077ee 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,7 +7,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ ObbFile_test.cpp \ - PollLoop_test.cpp \ + Looper_test.cpp \ String8_test.cpp shared_libraries := \ diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp new file mode 100644 index 0000000..afc92f8 --- /dev/null +++ b/libs/utils/tests/Looper_test.cpp @@ -0,0 +1,433 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include <utils/Looper.h> +#include <utils/Timers.h> +#include <utils/StopWatch.h> +#include <gtest/gtest.h> +#include <unistd.h> +#include <time.h> + +#include "TestHelpers.h" + +// # of milliseconds to fudge stopwatch measurements +#define TIMING_TOLERANCE_MS 25 + +namespace android { + +class DelayedWake : public DelayedTask { + sp<Looper> mLooper; + +public: + DelayedWake(int delayMillis, const sp<Looper> looper) : + DelayedTask(delayMillis), mLooper(looper) { + } + +protected: + virtual void doTask() { + mLooper->wake(); + } +}; + +class DelayedWriteSignal : public DelayedTask { + Pipe* mPipe; + +public: + DelayedWriteSignal(int delayMillis, Pipe* pipe) : + DelayedTask(delayMillis), mPipe(pipe) { + } + +protected: + virtual void doTask() { + mPipe->writeSignal(); + } +}; + +class CallbackHandler { +public: + void setCallback(const sp<Looper>& looper, int fd, int events) { + looper->addFd(fd, 0, events, staticHandler, this); + } + +protected: + virtual ~CallbackHandler() { } + + virtual int handler(int fd, int events) = 0; + +private: + static int staticHandler(int fd, int events, void* data) { + return static_cast<CallbackHandler*>(data)->handler(fd, events); + } +}; + +class StubCallbackHandler : public CallbackHandler { +public: + int nextResult; + int callbackCount; + + int fd; + int events; + + StubCallbackHandler(int nextResult) : nextResult(nextResult), + callbackCount(0), fd(-1), events(-1) { + } + +protected: + virtual int handler(int fd, int events) { + callbackCount += 1; + this->fd = fd; + this->events = events; + return nextResult; + } +}; + +class LooperTest : public testing::Test { +protected: + sp<Looper> mLooper; + + virtual void SetUp() { + mLooper = new Looper(true); + } + + virtual void TearDown() { + mLooper.clear(); + } +}; + + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) { + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) { + mLooper->wake(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because wake() was called before waiting"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) { + sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper); + delayedWake->run(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal wake delay"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) { + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + ASSERT_EQ(OK, pipe.writeSignal()); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + pipe.writeSignal(); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + delayedWriteSignal->run(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal signal delay"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + pipe.writeSignal(); // would cause FD to be considered signalled + mLooper->removeFd(pipe.receiveFd); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout because FD was no longer registered"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not be invoked"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { + Pipe pipe; + StubCallbackHandler handler(false); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + // First loop: Callback is registered and FD is signalled. + pipe.writeSignal(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because FD was already signalled"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked"; + + // Second loop: Callback is no longer registered and FD is signalled. + pipe.writeSignal(); + + stopWatch.reset(); + result = mLooper->pollOnce(0); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because timeout was zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should not be invoked this time"; +} + +TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) { + const int expectedIdent = 5; + void* expectedData = this; + + Pipe pipe; + + pipe.writeSignal(); + mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData); + + StopWatch stopWatch("pollOnce"); + int fd; + int events; + void* data; + int result = mLooper->pollOnce(100, &fd, &events, &data); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(expectedIdent, result) + << "pollOnce result should be the ident of the FD that was signalled"; + EXPECT_EQ(pipe.receiveFd, fd) + << "pollOnce should have returned the received pipe fd"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, events) + << "pollOnce should have returned ALOOPER_EVENT_INPUT as events"; + EXPECT_EQ(expectedData, data) + << "pollOnce should have returned the data"; +} + +TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL); + + EXPECT_EQ(1, result) + << "addFd should return 1 because FD was added"; +} + +TEST_F(LooperTest, AddFd_WhenEventsIsZero_ReturnsError) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) { + Pipe pipe; + sp<Looper> looper = new Looper(false /*allowNonCallbacks*/); + int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) { + int result = mLooper->removeFd(1); + + EXPECT_EQ(0, result) + << "removeFd should return 0 because FD not registered"; +} + +TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) { + Pipe pipe; + StubCallbackHandler handler(false); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + // First time. + int result = mLooper->removeFd(pipe.receiveFd); + + EXPECT_EQ(1, result) + << "removeFd should return 1 first time because FD was registered"; + + // Second time. + result = mLooper->removeFd(pipe.receiveFd); + + EXPECT_EQ(0, result) + << "removeFd should return 0 second time because FD was no longer registered"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { + Pipe pipe; + StubCallbackHandler handler1(true); + StubCallbackHandler handler2(true); + + handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it + pipe.writeSignal(); // would cause FD to be considered signalled + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because FD was already signalled"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(0, handler1.callbackCount) + << "original handler callback should not be invoked because it was replaced"; + EXPECT_EQ(1, handler2.callbackCount) + << "replacement handler callback should be invoked"; +} + + +} // namespace android diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp deleted file mode 100644 index 02f1808..0000000 --- a/libs/utils/tests/PollLoop_test.cpp +++ /dev/null @@ -1,370 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include <utils/PollLoop.h> -#include <utils/Timers.h> -#include <utils/StopWatch.h> -#include <gtest/gtest.h> -#include <unistd.h> -#include <time.h> - -#include "TestHelpers.h" - -// # of milliseconds to fudge stopwatch measurements -#define TIMING_TOLERANCE_MS 25 - -namespace android { - -class DelayedWake : public DelayedTask { - sp<PollLoop> mPollLoop; - -public: - DelayedWake(int delayMillis, const sp<PollLoop> pollLoop) : - DelayedTask(delayMillis), mPollLoop(pollLoop) { - } - -protected: - virtual void doTask() { - mPollLoop->wake(); - } -}; - -class DelayedWriteSignal : public DelayedTask { - Pipe* mPipe; - -public: - DelayedWriteSignal(int delayMillis, Pipe* pipe) : - DelayedTask(delayMillis), mPipe(pipe) { - } - -protected: - virtual void doTask() { - mPipe->writeSignal(); - } -}; - -class CallbackHandler { -public: - void setCallback(const sp<PollLoop>& pollLoop, int fd, int events) { - pollLoop->setCallback(fd, events, staticHandler, this); - } - -protected: - virtual ~CallbackHandler() { } - - virtual bool handler(int fd, int events) = 0; - -private: - static bool staticHandler(int fd, int events, void* data) { - return static_cast<CallbackHandler*>(data)->handler(fd, events); - } -}; - -class StubCallbackHandler : public CallbackHandler { -public: - bool nextResult; - int callbackCount; - - int fd; - int events; - - StubCallbackHandler(bool nextResult) : nextResult(nextResult), - callbackCount(0), fd(-1), events(-1) { - } - -protected: - virtual bool handler(int fd, int events) { - callbackCount += 1; - this->fd = fd; - this->events = events; - return nextResult; - } -}; - -class PollLoopTest : public testing::Test { -protected: - sp<PollLoop> mPollLoop; - - virtual void SetUp() { - mPollLoop = new PollLoop(false); - } - - virtual void TearDown() { - mPollLoop.clear(); - } -}; - - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { - mPollLoop->wake(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because loop was awoken"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { - sp<DelayedWake> delayedWake = new DelayedWake(100, mPollLoop); - delayedWake->run(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal wake delay"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because loop was awoken"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - - ASSERT_EQ(OK, pipe.writeSignal()); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - - pipe.writeSignal(); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - delayedWriteSignal->run(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal signal delay"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - pipe.writeSignal(); // would cause FD to be considered signalled - mPollLoop->removeCallback(pipe.receiveFd); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not be invoked"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { - Pipe pipe; - StubCallbackHandler handler(false); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - // First loop: Callback is registered and FD is signalled. - pipe.writeSignal(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked"; - - // Second loop: Callback is no longer registered and FD is signalled. - pipe.writeSignal(); - - stopWatch.reset(); - result = mPollLoop->pollOnce(0); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should not be invoked this time"; -} - -TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) { - bool result = mPollLoop->removeCallback(1); - - EXPECT_FALSE(result) - << "removeCallback should return false because FD not registered"; -} - -TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) { - Pipe pipe; - StubCallbackHandler handler(false); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - // First time. - bool result = mPollLoop->removeCallback(pipe.receiveFd); - - EXPECT_TRUE(result) - << "removeCallback should return true first time because FD was registered"; - - // Second time. - result = mPollLoop->removeCallback(pipe.receiveFd); - - EXPECT_FALSE(result) - << "removeCallback should return false second time because FD was no longer registered"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { - Pipe pipe; - StubCallbackHandler handler1(true); - StubCallbackHandler handler2(true); - - handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it - pipe.writeSignal(); // would cause FD to be considered signalled - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(0, handler1.callbackCount) - << "original handler callback should not be invoked because it was replaced"; - EXPECT_EQ(1, handler2.callbackCount) - << "replacement handler callback should be invoked"; -} - - -} // namespace android |