45e0acb41a
The ALooper API now uses an explicit "identifier" for the integer that is returned rather than implicitly using the fd. This allows the APIs that had the fd to be a little more sane. Change-Id: I8507f535ad484c0bdc4a1bd016d87bb09acd7ff0
372 lines
10 KiB
C++
372 lines
10 KiB
C++
//
|
|
// 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;
|
|
}
|
|
|
|
mLock.lock();
|
|
while (mWaiters != 0) {
|
|
mResume.wait(mLock);
|
|
}
|
|
mPolling = true;
|
|
mLock.unlock();
|
|
|
|
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
|
|
|
|
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:
|
|
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
|