summaryrefslogtreecommitdiffstats
path: root/libs/utils
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2010-09-13 23:17:30 -0700
committerJeff Brown <jeffbrown@google.com>2010-09-14 01:59:45 -0700
commit4fe6c3e51be77e35f40872cdbca6c80f8f8b7ecb (patch)
tree5cbcfad147ad1bf26deb384e41d27f4e6bfcdb80 /libs/utils
parentc891d2b3529b9cf24ef4781a585cd4784815e711 (diff)
downloadframeworks_base-4fe6c3e51be77e35f40872cdbca6c80f8f8b7ecb.zip
frameworks_base-4fe6c3e51be77e35f40872cdbca6c80f8f8b7ecb.tar.gz
frameworks_base-4fe6c3e51be77e35f40872cdbca6c80f8f8b7ecb.tar.bz2
Replace epoll() with poll() and rename PollLoop to Looper.
As part of this change, consolidated and cleaned up the Looper API so that there are fewer distinctions between the NDK and non-NDK declarations (no need for two callback types, etc.). Removed the dependence on specific constants from sys/poll.h such as POLLIN. Instead looper.h defines events like LOOPER_EVENT_INPUT for the events that it supports. That should help make any future under-the-hood implementation changes easier. Fixed a couple of compiler warnings along the way. Change-Id: I449a7ec780bf061bdd325452f823673e2b39b6ae
Diffstat (limited to 'libs/utils')
-rw-r--r--libs/utils/Android.mk2
-rw-r--r--libs/utils/Looper.cpp368
-rw-r--r--libs/utils/PollLoop.cpp377
-rw-r--r--libs/utils/tests/Android.mk2
-rw-r--r--libs/utils/tests/Looper_test.cpp433
-rw-r--r--libs/utils/tests/PollLoop_test.cpp370
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