diff options
27 files changed, 1290 insertions, 1184 deletions
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 2517a8a..6aa77f6 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -28,7 +28,7 @@ #include <surfaceflinger/Surface.h> #include <ui/egl/android_natives.h> #include <ui/InputTransport.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include "JNIHelp.h" #include "android_os_MessageQueue.h" @@ -128,17 +128,17 @@ AInputQueue::~AInputQueue() { } void AInputQueue::attachLooper(ALooper* looper, int ident, - ALooper_callbackFunc* callback, void* data) { - mPollLoop = static_cast<android::PollLoop*>(looper); - mPollLoop->setLooperCallback(mConsumer.getChannel()->getReceivePipeFd(), - ident, POLLIN, callback, data); - mPollLoop->setLooperCallback(mDispatchKeyRead, - ident, POLLIN, callback, data); + ALooper_callbackFunc callback, void* data) { + mLooper = static_cast<android::Looper*>(looper); + mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(), + ident, ALOOPER_EVENT_INPUT, callback, data); + mLooper->addFd(mDispatchKeyRead, + ident, ALOOPER_EVENT_INPUT, callback, data); } void AInputQueue::detachLooper() { - mPollLoop->removeCallback(mConsumer.getChannel()->getReceivePipeFd()); - mPollLoop->removeCallback(mDispatchKeyRead); + mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd()); + mLooper->removeFd(mDispatchKeyRead); } int32_t AInputQueue::hasEvents() { @@ -440,8 +440,8 @@ struct NativeCode : public ANativeActivity { if (env != NULL && clazz != NULL) { env->DeleteGlobalRef(clazz); } - if (pollLoop != NULL && mainWorkRead >= 0) { - pollLoop->removeCallback(mainWorkRead); + if (looper != NULL && mainWorkRead >= 0) { + looper->removeFd(mainWorkRead); } if (nativeInputQueue != NULL) { nativeInputQueue->mWorkWrite = -1; @@ -509,7 +509,7 @@ struct NativeCode : public ANativeActivity { // These are used to wake up the main thread to process work. int mainWorkRead; int mainWorkWrite; - sp<PollLoop> pollLoop; + sp<Looper> looper; }; void android_NativeActivity_setWindowFormat( @@ -541,15 +541,15 @@ void android_NativeActivity_hideSoftInput( /* * Callback for handling native events on the application's main thread. */ -static bool mainWorkCallback(int fd, int events, void* data) { +static int mainWorkCallback(int fd, int events, void* data) { NativeCode* code = (NativeCode*)data; if ((events & POLLIN) == 0) { - return true; + return 1; } ActivityWork work; if (!read_work(code->mainWorkRead, &work)) { - return true; + return 1; } LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); @@ -593,7 +593,7 @@ static bool mainWorkCallback(int fd, int events, void* data) { break; } - return true; + return 1; } // ------------------------------------------------------------------------ @@ -621,9 +621,9 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ return 0; } - code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue); - if (code->pollLoop == NULL) { - LOGW("Unable to retrieve MessageQueue's PollLoop"); + code->looper = android_os_MessageQueue_getLooper(env, messageQueue); + if (code->looper == NULL) { + LOGW("Unable to retrieve MessageQueue's Looper"); delete code; return 0; } @@ -642,7 +642,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK); SLOGW_IF(result != 0, "Could not make main work write pipe " "non-blocking: %s", strerror(errno)); - code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); + code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code); code->ANativeActivity::callbacks = &code->callbacks; if (env->GetJavaVM(&code->vm) < 0) { diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index e29495c..10ceb7b 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -60,7 +60,7 @@ sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint Sensor const* const* sensorList; size_t count = mgr.getSensorList(&sensorList); - if (next >= count) + if (size_t(next) >= count) return -1; Sensor const* const list = sensorList[next]; @@ -78,7 +78,7 @@ sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint env->SetIntField(sensor, sensorOffsets.minDelay, list->getMinDelay()); next++; - return next<count ? next : 0; + return size_t(next) < count ? next : 0; } //---------------------------------------------------------------------------- diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp index 847b5a5..1b203ca 100644 --- a/core/jni/android_os_MessageQueue.cpp +++ b/core/jni/android_os_MessageQueue.cpp @@ -18,7 +18,7 @@ #include "JNIHelp.h" -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/Log.h> #include "android_os_MessageQueue.h" @@ -39,22 +39,22 @@ public: NativeMessageQueue(); ~NativeMessageQueue(); - inline sp<PollLoop> getPollLoop() { return mPollLoop; } + inline sp<Looper> getLooper() { return mLooper; } bool pollOnce(int timeoutMillis); void wake(); private: - sp<PollLoop> mPollLoop; + sp<Looper> mLooper; }; // ---------------------------------------------------------------------------- NativeMessageQueue::NativeMessageQueue() { - mPollLoop = PollLoop::getForThread(); - if (mPollLoop == NULL) { - mPollLoop = new PollLoop(false); - PollLoop::setForThread(mPollLoop); + mLooper = Looper::getForThread(); + if (mLooper == NULL) { + mLooper = new Looper(false); + Looper::setForThread(mLooper); } } @@ -62,11 +62,11 @@ NativeMessageQueue::~NativeMessageQueue() { } bool NativeMessageQueue::pollOnce(int timeoutMillis) { - return mPollLoop->pollOnce(timeoutMillis) != PollLoop::POLL_TIMEOUT; + return mLooper->pollOnce(timeoutMillis) != ALOOPER_POLL_TIMEOUT; } void NativeMessageQueue::wake() { - mPollLoop->wake(); + mLooper->wake(); } // ---------------------------------------------------------------------------- @@ -83,10 +83,10 @@ static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject m reinterpret_cast<jint>(nativeMessageQueue)); } -sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj) { +sp<Looper> android_os_MessageQueue_getLooper(JNIEnv* env, jobject messageQueueObj) { NativeMessageQueue* nativeMessageQueue = android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj); - return nativeMessageQueue != NULL ? nativeMessageQueue->getPollLoop() : NULL; + return nativeMessageQueue != NULL ? nativeMessageQueue->getLooper() : NULL; } static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { diff --git a/core/jni/android_os_MessageQueue.h b/core/jni/android_os_MessageQueue.h index 5c742e2..f961d8f 100644 --- a/core/jni/android_os_MessageQueue.h +++ b/core/jni/android_os_MessageQueue.h @@ -21,9 +21,9 @@ namespace android { -class PollLoop; +class Looper; -extern sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj); +extern sp<Looper> android_os_MessageQueue_getLooper(JNIEnv* env, jobject messageQueueObj); } // namespace android diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp index 42f35d1..282e9ed 100644 --- a/core/jni/android_view_InputQueue.cpp +++ b/core/jni/android_view_InputQueue.cpp @@ -29,7 +29,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/KeyedVector.h> #include <utils/threads.h> #include <ui/InputTransport.h> @@ -77,7 +77,7 @@ private: }; Connection(uint16_t id, - const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop); + const sp<InputChannel>& inputChannel, const sp<Looper>& looper); inline const char* getInputChannelName() const { return inputChannel->getName().string(); } @@ -88,7 +88,7 @@ private: sp<InputChannel> inputChannel; InputConsumer inputConsumer; - sp<PollLoop> pollLoop; + sp<Looper> looper; jobject inputHandlerObjGlobal; PreallocatedInputEventFactory inputEventFactory; @@ -110,7 +110,7 @@ private: static void handleInputChannelDisposed(JNIEnv* env, jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data); - static bool handleReceiveCallback(int receiveFd, int events, void* data); + static int handleReceiveCallback(int receiveFd, int events, void* data); static jlong generateFinishedToken(int32_t receiveFd, uint16_t connectionId, uint16_t messageSeqNum); @@ -141,7 +141,7 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne LOGD("channel '%s' - Registered", inputChannel->getName().string()); #endif - sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj); + sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); { // acquire lock AutoMutex _l(mLock); @@ -153,7 +153,7 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne } uint16_t connectionId = mNextConnectionId++; - sp<Connection> connection = new Connection(connectionId, inputChannel, pollLoop); + sp<Connection> connection = new Connection(connectionId, inputChannel, looper); status_t result = connection->inputConsumer.initialize(); if (result) { LOGW("Failed to initialize input consumer for input channel '%s', status=%d", @@ -166,7 +166,7 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne int32_t receiveFd = inputChannel->getReceivePipeFd(); mConnectionsByReceiveFd.add(receiveFd, connection); - pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); + looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock android_view_InputChannel_setDisposeCallback(env, inputChannelObj, @@ -201,7 +201,7 @@ status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChan connection->status = Connection::STATUS_ZOMBIE; - connection->pollLoop->removeCallback(inputChannel->getReceivePipeFd()); + connection->looper->removeFd(inputChannel->getReceivePipeFd()); env->DeleteGlobalRef(connection->inputHandlerObjGlobal); connection->inputHandlerObjGlobal = NULL; @@ -293,7 +293,7 @@ void NativeInputQueue::handleInputChannelDisposed(JNIEnv* env, q->unregisterInputChannel(env, inputChannelObj); } -bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) { +int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) { NativeInputQueue* q = static_cast<NativeInputQueue*>(data); JNIEnv* env = AndroidRuntime::getJNIEnv(); @@ -308,33 +308,33 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da if (connectionIndex < 0) { LOGE("Received spurious receive callback for unknown input channel. " "fd=%d, events=0x%x", receiveFd, events); - return false; // remove the callback + return 0; // remove the callback } connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex); - if (events & (POLLERR | POLLHUP | POLLNVAL)) { + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. " "events=0x%x", connection->getInputChannelName(), events); - return false; // remove the callback + return 0; // remove the callback } - if (! (events & POLLIN)) { + if (! (events & ALOOPER_EVENT_INPUT)) { LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", connection->getInputChannelName(), events); - return true; + return 1; } status_t status = connection->inputConsumer.receiveDispatchSignal(); if (status) { LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", connection->getInputChannelName(), status); - return false; // remove the callback + return 0; // remove the callback } if (connection->messageInProgress) { LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.", connection->getInputChannelName()); - return true; + return 1; } status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent); @@ -342,7 +342,7 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da LOGW("channel '%s' ~ Failed to consume input event. status=%d", connection->getInputChannelName(), status); connection->inputConsumer.sendFinishedSignal(); - return true; + return 1; } connection->messageInProgress = true; @@ -394,7 +394,7 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da connection->getInputChannelName()); env->DeleteLocalRef(inputHandlerObjLocal); q->finished(env, finishedToken, false); - return true; + return 1; } #if DEBUG_DISPATCH_CYCLE @@ -417,7 +417,7 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da env->DeleteLocalRef(inputEventObj); env->DeleteLocalRef(inputHandlerObjLocal); - return true; + return 1; } jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId, @@ -435,9 +435,9 @@ void NativeInputQueue::parseFinishedToken(jlong finishedToken, // ---------------------------------------------------------------------------- NativeInputQueue::Connection::Connection(uint16_t id, - const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) : + const sp<InputChannel>& inputChannel, const sp<Looper>& looper) : id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel), - pollLoop(pollLoop), inputHandlerObjGlobal(NULL), + looper(looper), inputHandlerObjGlobal(NULL), messageSeqNum(0), messageInProgress(false) { } diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h index fdceb84..b49e02a 100644 --- a/include/android_runtime/android_app_NativeActivity.h +++ b/include/android_runtime/android_app_NativeActivity.h @@ -18,6 +18,7 @@ #define _ANDROID_APP_NATIVEACTIVITY_H #include <ui/InputTransport.h> +#include <utils/Looper.h> #include <android/native_activity.h> @@ -69,7 +70,7 @@ public: /* Destroys the consumer and releases its input channel. */ ~AInputQueue(); - void attachLooper(ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data); + void attachLooper(ALooper* looper, int ident, ALooper_callbackFunc callback, void* data); void detachLooper(); @@ -103,7 +104,7 @@ private: void wakeupDispatch(); android::InputConsumer mConsumer; - android::sp<android::PollLoop> mPollLoop; + android::sp<android::Looper> mLooper; int mDispatchKeyRead; int mDispatchKeyWrite; diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h index 6581ae3..97dd391 100644 --- a/include/gui/SensorEventQueue.h +++ b/include/gui/SensorEventQueue.h @@ -42,7 +42,7 @@ namespace android { class ISensorEventConnection; class Sensor; -class PollLoop; +class Looper; // ---------------------------------------------------------------------------- @@ -69,11 +69,11 @@ public: status_t disableSensor(int32_t handle) const; private: - sp<PollLoop> getPollLoop() const; + sp<Looper> getLooper() const; sp<ISensorEventConnection> mSensorEventConnection; sp<SensorChannel> mSensorChannel; mutable Mutex mLock; - mutable sp<PollLoop> mPollLoop; + mutable sp<Looper> mLooper; }; // ---------------------------------------------------------------------------- diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index a06208a..d7e6254 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -25,7 +25,7 @@ #include <utils/Timers.h> #include <utils/RefBase.h> #include <utils/String8.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/Pool.h> #include <stddef.h> @@ -826,7 +826,7 @@ private: Mutex mLock; Allocator mAllocator; - sp<PollLoop> mPollLoop; + sp<Looper> mLooper; EventEntry* mPendingEvent; Queue<EventEntry> mInboundQueue; @@ -837,7 +837,7 @@ private: void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime); - // Enqueues an inbound event. Returns true if mPollLoop->wake() should be called. + // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(EventEntry* entry); // App switch latency optimization. @@ -1010,7 +1010,7 @@ private: void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool broken); void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain); - static bool handleReceiveCallback(int receiveFd, int events, void* data); + static int handleReceiveCallback(int receiveFd, int events, void* data); // Preempting input dispatch. bool preemptInputDispatchInnerLocked(); diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index 82831e2..dc9e27a 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -33,7 +33,6 @@ #include <semaphore.h> #include <ui/Input.h> #include <utils/Errors.h> -#include <utils/PollLoop.h> #include <utils/Timers.h> #include <utils/RefBase.h> #include <utils/String8.h> diff --git a/include/utils/Looper.h b/include/utils/Looper.h new file mode 100644 index 0000000..92e4b0a --- /dev/null +++ b/include/utils/Looper.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_LOOPER_H +#define UTILS_LOOPER_H + +#include <utils/threads.h> +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> + +#include <android/looper.h> + +/* + * Declare a concrete type for the NDK's looper forward declaration. + */ +struct ALooper { +}; + +namespace android { + +/** + * A polling loop that supports monitoring file descriptor events, optionally + * using callbacks. The implementation uses epoll() internally. + * + * A looper can be associated with a thread although there is no requirement that it must be. + */ +class Looper : public ALooper, public RefBase { +protected: + virtual ~Looper(); + +public: + /** + * Creates a looper. + * + * If allowNonCallbaks is true, the looper will allow file descriptors to be + * registered without associated callbacks. This assumes that the caller of + * pollOnce() is prepared to handle callback-less events itself. + */ + Looper(bool allowNonCallbacks); + + /** + * Returns whether this looper instance allows the registration of file descriptors + * using identifiers instead of callbacks. + */ + bool getAllowNonCallbacks() const; + + /** + * Waits for events to be available, with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until an event appears. + * + * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before + * the timeout expired and no callbacks were invoked and no other file + * descriptors were ready. + * + * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing an identifier if its file descriptor has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outFd, outEvents and outData will contain the poll + * events and data associated with the fd, otherwise they will be set to NULL. + * + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ + int pollOnce(int timeoutMillis, + int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + + /** + * Like pollOnce(), but performs all pending callbacks until all + * data has been consumed or a file descriptor is available with no callback. + * This function will never return ALOOPER_POLL_CALLBACK. + */ + int pollAll(int timeoutMillis, + int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + + /** + * Wakes the poll asynchronously. + * + * This method can be called on any thread. + * This method returns immediately. + */ + void wake(); + + /** + * Adds a new file descriptor to be polled by the looper. + * If the same file descriptor was previously added, it is replaced. + * + * "fd" is the file descriptor to be added. + * "ident" is an identifier for this event, which is returned from ALooper_pollOnce(). + * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. + * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. + * "callback" is the function to call when there is an event on the file descriptor. + * "data" is a private data pointer to supply to the callback. + * + * There are two main uses of this function: + * + * (1) If "callback" is non-NULL, then this function will be called when there is + * data on the file descriptor. It should execute any events it has pending, + * appropriately reading from the file descriptor. The 'ident' is ignored in this case. + * + * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce + * when its file descriptor has data available, requiring the caller to take + * care of processing it. + * + * Returns 1 if the file descriptor was added, 0 if the arguments were invalid. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. + */ + int addFd(int fd, int ident, + int events, ALooper_callbackFunc callback, void* data = NULL); + + /** + * Removes a previously added file descriptor from the looper. + * + * When this method returns, it is safe to close the file descriptor since the looper + * will no longer have a reference to it. However, it is possible for the callback to + * already be running or for it to run one last time if the file descriptor was already + * signalled. Calling code is responsible for ensuring that this case is safely handled. + * For example, if the callback takes care of removing itself during its own execution either + * by returning 0 or by calling this method, then it can be guaranteed to not be invoked + * again at any later time unless registered anew. + * + * Returns 1 if the file descriptor was removed, 0 if none was previously registered. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. + */ + int removeFd(int fd); + + /** + * Prepares a looper associated with the calling thread, and returns it. + * If the thread already has a looper, it is returned. Otherwise, a new + * one is created, associated with the thread, and returned. + * + * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. + */ + static sp<Looper> prepare(int opts); + + /** + * Sets the given looper to be associated with the calling thread. + * If another looper is already associated with the thread, it is replaced. + * + * If "looper" is NULL, removes the currently associated looper. + */ + static void setForThread(const sp<Looper>& looper); + + /** + * Returns the looper associated with the calling thread, or NULL if + * there is not one. + */ + static sp<Looper> getForThread(); + +private: + struct Request { + int fd; + int ident; + ALooper_callbackFunc callback; + void* data; + }; + + struct Response { + int events; + Request request; + }; + + const bool mAllowNonCallbacks; // immutable + + int mEpollFd; // immutable + int mWakeReadPipeFd; // immutable + int mWakeWritePipeFd; // immutable + + // Locked list of file descriptor monitoring requests. + Mutex mLock; + KeyedVector<int, Request> mRequests; + + // This state is only used privately by pollOnce and does not require a lock since + // it runs on a single thread. + Vector<Response> mResponses; + size_t mResponseIndex; + + int pollInner(int timeoutMillis); + + static void threadDestructor(void *st); +}; + +} // namespace android + +#endif // UTILS_LOOPER_H diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h deleted file mode 100644 index c2dfe5d..0000000 --- a/include/utils/PollLoop.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UTILS_POLL_LOOP_H -#define UTILS_POLL_LOOP_H - -#include <utils/Vector.h> -#include <utils/threads.h> - -#include <sys/poll.h> - -#include <android/looper.h> - -struct ALooper : public android::RefBase { -protected: - virtual ~ALooper() { } - -public: - ALooper() { } -}; - -namespace android { - -/** - * A basic file descriptor polling loop based on poll() with callbacks. - */ -class PollLoop : public ALooper { -protected: - virtual ~PollLoop(); - -public: - PollLoop(bool allowNonCallbacks); - - /** - * A callback that it to be invoked when an event occurs on a file descriptor. - * Specifies the events that were triggered and the user data provided when the - * callback was set. - * - * Returns true if the callback should be kept, false if it should be removed automatically - * after the callback returns. - */ - typedef bool (*Callback)(int fd, int events, void* data); - - enum { - POLL_CALLBACK = ALOOPER_POLL_CALLBACK, - POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT, - POLL_ERROR = ALOOPER_POLL_ERROR, - }; - - /** - * Performs a single call to poll() with optional timeout in milliseconds. - * Invokes callbacks for all file descriptors on which an event occurred. - * - * If the timeout is zero, returns immediately without blocking. - * If the timeout is negative, waits indefinitely until awoken. - * - * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. - * - * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given - * timeout expired. - * - * Returns ALOPER_POLL_ERROR if an error occurred. - * - * Returns a value >= 0 containing a file descriptor if it has data - * and it has no callback function (requiring the caller here to handle it). - * In this (and only this) case outEvents and outData will contain the poll - * events and data associated with the fd. - * - * This method must only be called on the thread owning the PollLoop. - * This method blocks until either a file descriptor is signalled, a timeout occurs, - * or wake() is called. - * This method does not return until it has finished invoking the appropriate callbacks - * for all file descriptors that were signalled. - */ - int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL); - - /** - * Wakes the loop asynchronously. - * - * This method can be called on any thread. - * This method returns immediately. - */ - void wake(); - - /** - * Control whether this PollLoop instance allows using IDs instead - * of callbacks. - */ - bool getAllowNonCallbacks() const; - - /** - * Sets the callback for a file descriptor, replacing the existing one, if any. - * It is an error to call this method with events == 0 or callback == NULL. - * - * Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events - * even if it is not explicitly requested when registered. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll loop. - */ - void setCallback(int fd, int ident, int events, Callback callback, void* data = NULL); - - /** - * Convenience for above setCallback when ident is not used. In this case - * the ident is set to POLL_CALLBACK. - */ - void setCallback(int fd, int events, Callback callback, void* data = NULL); - - /** - * Like setCallback(), but for the NDK callback function. - */ - void setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, - void* data); - - /** - * Removes the callback for a file descriptor, if one exists. - * - * When this method returns, it is safe to close the file descriptor since the poll loop - * will no longer have a reference to it. However, it is possible for the callback to - * already be running or for it to run one last time if the file descriptor was already - * signalled. Calling code is responsible for ensuring that this case is safely handled. - * For example, if the callback takes care of removing itself during its own execution either - * by returning false or calling this method, then it can be guaranteed to not be invoked - * again at any later time unless registered anew. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll loop. - * - * Returns true if a callback was actually removed, false if none was registered. - */ - bool removeCallback(int fd); - - /** - * Set the given PollLoop to be associated with the - * calling thread. There must be a 1:1 relationship between - * PollLoop and thread. - */ - static void setForThread(const sp<PollLoop>& pollLoop); - - /** - * Return the PollLoop associated with the calling thread. - */ - static sp<PollLoop> getForThread(); - -private: - struct RequestedCallback { - Callback callback; - ALooper_callbackFunc* looperCallback; - int ident; - void* data; - }; - - struct PendingCallback { - int fd; - int ident; - int events; - Callback callback; - ALooper_callbackFunc* looperCallback; - void* data; - }; - - const bool mAllowNonCallbacks; // immutable - - int mWakeReadPipeFd; // immutable - int mWakeWritePipeFd; // immutable - - // The lock guards state used to track whether there is a poll() in progress and whether - // there are any other threads waiting in wakeAndLock(). The condition variables - // are used to transfer control among these threads such that all waiters are - // serviced before a new poll can begin. - // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake - // until mPolling becomes false, then decrements mWaiters again. - // The poll() method blocks on mResume until mWaiters becomes 0, then sets - // mPolling to true, blocks until the poll completes, then resets mPolling to false - // and signals mResume if there are waiters. - Mutex mLock; - bool mPolling; // guarded by mLock - uint32_t mWaiters; // guarded by mLock - Condition mAwake; // guarded by mLock - Condition mResume; // guarded by mLock - - // The next two vectors are only mutated when mPolling is false since they must - // not be changed while the poll() system call is in progress. To mutate these - // vectors, the poll() must first be awoken then the lock acquired. - Vector<struct pollfd> mRequestedFds; - Vector<RequestedCallback> mRequestedCallbacks; - - // This state is only used privately by pollOnce and does not require a lock since - // it runs on a single thread. - Vector<PendingCallback> mPendingCallbacks; - Vector<PendingCallback> mPendingFds; - size_t mPendingFdsPos; - - void openWakePipe(); - void closeWakePipe(); - - void setCallbackCommon(int fd, int ident, int events, Callback callback, - ALooper_callbackFunc* looperCallback, void* data); - ssize_t getRequestIndexLocked(int fd); - void wakeAndLock(); - static void threadDestructor(void *st); -}; - -} // namespace android - -#endif // UTILS_POLL_LOOP_H diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp index 7eb6da5..c3a9f22 100644 --- a/libs/gui/SensorEventQueue.cpp +++ b/libs/gui/SensorEventQueue.cpp @@ -21,7 +21,7 @@ #include <utils/Errors.h> #include <utils/RefBase.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <gui/Sensor.h> #include <gui/SensorChannel.h> @@ -81,28 +81,28 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) return size; } -sp<PollLoop> SensorEventQueue::getPollLoop() const +sp<Looper> SensorEventQueue::getLooper() const { Mutex::Autolock _l(mLock); - if (mPollLoop == 0) { - mPollLoop = new PollLoop(true); - mPollLoop->setCallback(getFd(), getFd(), POLLIN, NULL, NULL); + if (mLooper == 0) { + mLooper = new Looper(true); + mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL); } - return mPollLoop; + return mLooper; } status_t SensorEventQueue::waitForEvent() const { const int fd = getFd(); - sp<PollLoop> pollLoop(getPollLoop()); - int32_t result = pollLoop->pollOnce(-1, NULL, NULL); - return (result == fd) ? NO_ERROR : -1; + sp<Looper> looper(getLooper()); + int32_t result = looper->pollOnce(-1); + return (result == fd) ? status_t(NO_ERROR) : status_t(-1); } status_t SensorEventQueue::wake() const { - sp<PollLoop> pollLoop(getPollLoop()); - pollLoop->wake(); + sp<Looper> looper(getLooper()); + looper->wake(); return NO_ERROR; } diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index b8a26b0..48dea57 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -95,7 +95,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mFocusedApplication(NULL), mCurrentInputTargetsValid(false), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { - mPollLoop = new PollLoop(false); + mLooper = new Looper(false); mInboundQueue.headSentinel.refCount = -1; mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL; @@ -156,7 +156,7 @@ void InputDispatcher::dispatchOnce() { timeoutMillis = 0; } - mPollLoop->pollOnce(timeoutMillis); + mLooper->pollOnce(timeoutMillis); } void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, @@ -1784,7 +1784,7 @@ void InputDispatcher::drainOutboundQueueLocked(Connection* connection, } } -bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { +int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { InputDispatcher* d = static_cast<InputDispatcher*>(data); { // acquire lock @@ -1794,24 +1794,24 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat if (connectionIndex < 0) { LOGE("Received spurious receive callback for unknown input channel. " "fd=%d, events=0x%x", receiveFd, events); - return false; // remove the callback + return 0; // remove the callback } nsecs_t currentTime = now(); sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); - if (events & (POLLERR | POLLHUP | POLLNVAL)) { + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " "events=0x%x", connection->getInputChannelName(), events); d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); d->runCommandsLockedInterruptible(); - return false; // remove the callback + return 0; // remove the callback } - if (! (events & POLLIN)) { + if (! (events & ALOOPER_EVENT_INPUT)) { LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", connection->getInputChannelName(), events); - return true; + return 1; } status_t status = connection->inputPublisher.receiveFinishedSignal(); @@ -1820,12 +1820,12 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat connection->getInputChannelName(), status); d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); d->runCommandsLockedInterruptible(); - return false; // remove the callback + return 0; // remove the callback } d->finishDispatchCycleLocked(currentTime, connection); d->runCommandsLockedInterruptible(); - return true; + return 1; } // release lock } @@ -1843,7 +1843,7 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } } @@ -1870,7 +1870,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } } @@ -2007,7 +2007,7 @@ NoBatchingOrStreaming:; } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } } @@ -2043,7 +2043,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } int32_t injectionResult; @@ -2294,7 +2294,7 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { } // release lock // Wake up poll loop since it may need to make new input dispatching choices. - mPollLoop->wake(); + mLooper->wake(); } void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) { @@ -2317,7 +2317,7 @@ void InputDispatcher::setFocusedApplication(const InputApplication* inputApplica } // release lock // Wake up poll loop since it may need to make new input dispatching choices. - mPollLoop->wake(); + mLooper->wake(); } void InputDispatcher::releaseFocusedApplicationLocked() { @@ -2355,7 +2355,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { if (changed) { // Wake up poll loop since it may need to make new input dispatching choices. - mPollLoop->wake(); + mLooper->wake(); } } @@ -2372,7 +2372,7 @@ void InputDispatcher::preemptInputDispatch() { if (preemptedOne) { // Wake up the poll loop so it can get a head start dispatching the next event. - mPollLoop->wake(); + mLooper->wake(); } } @@ -2495,7 +2495,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan mMonitoringChannels.push(inputChannel); } - mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); + mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); runCommandsLockedInterruptible(); } // release lock @@ -2529,7 +2529,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh } } - mPollLoop->removeCallback(inputChannel->getReceivePipeFd()); + mLooper->removeFd(inputChannel->getReceivePipeFd()); nsecs_t currentTime = now(); abortDispatchCycleLocked(currentTime, connection, true /*broken*/); @@ -2539,7 +2539,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh // Wake the poll loop because removing the connection may have changed the current // synchronization state. - mPollLoop->wake(); + mLooper->wake(); return OK; } 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 diff --git a/native/android/input.cpp b/native/android/input.cpp index 57f0072..c753aa5 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -20,7 +20,7 @@ #include <android/input.h> #include <ui/Input.h> #include <ui/InputTransport.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/Vector.h> @@ -250,7 +250,7 @@ float AMotionEvent_getHistoricalOrientation(AInputEvent* motion_event, size_t po void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - int ident, ALooper_callbackFunc* callback, void* data) { + int ident, ALooper_callbackFunc callback, void* data) { queue->attachLooper(looper, ident, callback, data); } diff --git a/native/android/looper.cpp b/native/android/looper.cpp index 0aeed77..9f5cda9 100644 --- a/native/android/looper.cpp +++ b/native/android/looper.cpp @@ -18,65 +18,56 @@ #include <utils/Log.h> #include <android/looper.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> -using android::PollLoop; +using android::Looper; using android::sp; ALooper* ALooper_forThread() { - return PollLoop::getForThread().get(); + return Looper::getForThread().get(); } -ALooper* ALooper_prepare(int32_t opts) { - bool allowFds = (opts&ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) != 0; - sp<PollLoop> loop = PollLoop::getForThread(); - if (loop == NULL) { - loop = new PollLoop(allowFds); - PollLoop::setForThread(loop); - } - if (loop->getAllowNonCallbacks() != allowFds) { - LOGW("ALooper_prepare again with different ALOOPER_PREPARE_ALLOW_NON_CALLBACKS"); - } - return loop.get(); +ALooper* ALooper_prepare(int opts) { + return Looper::prepare(opts).get(); } -int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData) { - sp<PollLoop> loop = PollLoop::getForThread(); - if (loop == NULL) { - LOGW("ALooper_pollOnce: No looper for this thread!"); - return -1; - } - return loop->pollOnce(timeoutMillis, outEvents, outData); +void ALooper_acquire(ALooper* looper) { + static_cast<Looper*>(looper)->incStrong((void*)ALooper_acquire); } -int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData) { - sp<PollLoop> loop = PollLoop::getForThread(); - if (loop == NULL) { - LOGW("ALooper_pollOnce: No looper for this thread!"); - return -1; - } - - int32_t result; - while ((result = loop->pollOnce(timeoutMillis, outEvents, outData)) == ALOOPER_POLL_CALLBACK) { - ; +void ALooper_release(ALooper* looper) { + static_cast<Looper*>(looper)->decStrong((void*)ALooper_acquire); +} + +int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + sp<Looper> looper = Looper::getForThread(); + if (looper == NULL) { + LOGE("ALooper_pollOnce: No looper for this thread!"); + return ALOOPER_POLL_ERROR; } - - return result; + + return looper->pollOnce(timeoutMillis, outFd, outEvents, outData); } -void ALooper_acquire(ALooper* looper) { - static_cast<PollLoop*>(looper)->incStrong((void*)ALooper_acquire); +int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + sp<Looper> looper = Looper::getForThread(); + if (looper == NULL) { + LOGE("ALooper_pollAll: No looper for this thread!"); + return ALOOPER_POLL_ERROR; + } + + return looper->pollAll(timeoutMillis, outFd, outEvents, outData); } -void ALooper_release(ALooper* looper) { - static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire); +void ALooper_wake(ALooper* looper) { + static_cast<Looper*>(looper)->wake(); } -void ALooper_addFd(ALooper* looper, int fd, int ident, int events, - ALooper_callbackFunc* callback, void* data) { - static_cast<PollLoop*>(looper)->setLooperCallback(fd, ident, events, callback, data); +int ALooper_addFd(ALooper* looper, int fd, int ident, int events, + ALooper_callbackFunc callback, void* data) { + return static_cast<Looper*>(looper)->addFd(fd, ident, events, callback, data); } -int32_t ALooper_removeFd(ALooper* looper, int fd) { - return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0; +int ALooper_removeFd(ALooper* looper, int fd) { + return static_cast<Looper*>(looper)->removeFd(fd); } diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp index cf7635d..76c6eda 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -21,7 +21,7 @@ #include <android/sensor.h> #include <utils/RefBase.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/Timers.h> #include <gui/Sensor.h> @@ -60,12 +60,12 @@ ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type } ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager, - ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data) + ALooper* looper, int ident, ALooper_callbackFunc callback, void* data) { sp<SensorEventQueue> queue = static_cast<SensorManager*>(manager)->createEventQueue(); if (queue != 0) { - ALooper_addFd(looper, queue->getFd(), ident, POLLIN, callback, data); + ALooper_addFd(looper, queue->getFd(), ident, ALOOPER_EVENT_INPUT, callback, data); queue->looper = looper; queue->incStrong(manager); } diff --git a/native/include/android/input.h b/native/include/android/input.h index 9da122b..c1134bf 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -645,7 +645,7 @@ typedef struct AInputQueue AInputQueue; * ALooper_addFd() for information on the ident, callback, and data params. */ void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - int ident, ALooper_callbackFunc* callback, void* data); + int ident, ALooper_callbackFunc callback, void* data); /* * Remove the input queue from the looper it is currently attached to. diff --git a/native/include/android/looper.h b/native/include/android/looper.h index 287bcd5..a63b744 100644 --- a/native/include/android/looper.h +++ b/native/include/android/looper.h @@ -18,8 +18,6 @@ #ifndef ANDROID_LOOPER_H #define ANDROID_LOOPER_H -#include <poll.h> - #ifdef __cplusplus extern "C" { #endif @@ -41,25 +39,14 @@ struct ALooper; typedef struct ALooper ALooper; /** - * For callback-based event loops, this is the prototype of the function - * that is called. It is given the file descriptor it is associated with, - * a bitmask of the poll events that were triggered (typically POLLIN), and - * the data pointer that was originally supplied. - * - * Implementations should return 1 to continue receiving callbacks, or 0 - * to have this file descriptor and callback unregistered from the looper. - */ -typedef int ALooper_callbackFunc(int fd, int events, void* data); - -/** - * Return the ALooper associated with the calling thread, or NULL if + * Returns the looper associated with the calling thread, or NULL if * there is not one. */ ALooper* ALooper_forThread(); enum { /** - * Option for ALooper_prepare: this ALooper will accept calls to + * Option for ALooper_prepare: this looper will accept calls to * ALooper_addFd() that do not have a callback (that is provide NULL * for the callback). In this case the caller of ALooper_pollOnce() * or ALooper_pollAll() MUST check the return from these functions to @@ -69,108 +56,188 @@ enum { }; /** - * Prepare an ALooper associated with the calling thread, and return it. - * If the thread already has an ALooper, it is returned. Otherwise, a new + * Prepares a looper associated with the calling thread, and returns it. + * If the thread already has a looper, it is returned. Otherwise, a new * one is created, associated with the thread, and returned. * * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. */ -ALooper* ALooper_prepare(int32_t opts); +ALooper* ALooper_prepare(int opts); enum { /** - * Result from ALooper_pollOnce() and ALooper_pollAll(): one or - * more callbacks were executed. + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * The poll was awoken using wake() before the timeout expired + * and no callbacks were executed and no other file descriptors were ready. */ - ALOOPER_POLL_CALLBACK = -1, - + ALOOPER_POLL_WAKE = -1, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * One or more callbacks were executed. + */ + ALOOPER_POLL_CALLBACK = -2, + /** - * Result from ALooper_pollOnce() and ALooper_pollAll(): the - * timeout expired. + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * The timeout expired. */ - ALOOPER_POLL_TIMEOUT = -2, - + ALOOPER_POLL_TIMEOUT = -3, + /** - * Result from ALooper_pollOnce() and ALooper_pollAll(): an error - * occurred. + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * An error occurred. */ - ALOOPER_POLL_ERROR = -3, + ALOOPER_POLL_ERROR = -4, }; /** - * Wait for events to be available, with optional timeout in milliseconds. + * Acquire a reference on the given ALooper object. This prevents the object + * from being deleted until the reference is removed. This is only needed + * to safely hand an ALooper from one thread to another. + */ +void ALooper_acquire(ALooper* looper); + +/** + * Remove a reference that was previously acquired with ALooper_acquire(). + */ +void ALooper_release(ALooper* looper); + +/** + * Flags for file descriptor events that a looper can monitor. + * + * These flag bits can be combined to monitor multiple events at once. + */ +enum { + /** + * The file descriptor is available for read operations. + */ + ALOOPER_EVENT_INPUT = 1 << 0, + + /** + * The file descriptor is available for write operations. + */ + ALOOPER_EVENT_OUTPUT = 1 << 1, + + /** + * The file descriptor has encountered an error condition. + * + * The looper always sends notifications about errors; it is not necessary + * to specify this event flag in the requested event set. + */ + ALOOPER_EVENT_ERROR = 1 << 2, + + /** + * The file descriptor was hung up. + * For example, indicates that the remote end of a pipe or socket was closed. + * + * The looper always sends notifications about hangups; it is not necessary + * to specify this event flag in the requested event set. + */ + ALOOPER_EVENT_HANGUP = 1 << 3, +}; + +/** + * For callback-based event loops, this is the prototype of the function + * that is called. It is given the file descriptor it is associated with, + * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT), + * and the data pointer that was originally supplied. + * + * Implementations should return 1 to continue receiving callbacks, or 0 + * to have this file descriptor and callback unregistered from the looper. + */ +typedef int (*ALooper_callbackFunc)(int fd, int events, void* data); + +/** + * Waits for events to be available, with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. * * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until an event appears. * - * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before + * the timeout expired and no callbacks were invoked and no other file + * descriptors were ready. + * + * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. * * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given * timeout expired. * - * Returns ALOPER_POLL_ERROR if an error occurred. + * Returns ALOOPER_POLL_ERROR if an error occurred. * * Returns a value >= 0 containing an identifier if its file descriptor has data * and it has no callback function (requiring the caller here to handle it). - * In this (and only this) case outEvents and outData will contain the poll - * events and data associated with the fd. + * In this (and only this) case outFd, outEvents and outData will contain the poll + * events and data associated with the fd, otherwise they will be set to NULL. * * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ -int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData); +int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); /** * Like ALooper_pollOnce(), but performs all pending callbacks until all * data has been consumed or a file descriptor is available with no callback. * This function will never return ALOOPER_POLL_CALLBACK. */ -int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData); - -/** - * Acquire a reference on the given ALooper object. This prevents the object - * from being deleted until the reference is removed. This is only needed - * to safely hand an ALooper from one thread to another. - */ -void ALooper_acquire(ALooper* looper); +int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); /** - * Remove a reference that was previously acquired with ALooper_acquire(). + * Wakes the poll asynchronously. + * + * This method can be called on any thread. + * This method returns immediately. */ -void ALooper_release(ALooper* looper); +void ALooper_wake(ALooper* looper); /** - * Add a new file descriptor to be polled by the looper. If the same file - * descriptor was previously added, it is replaced. + * Adds a new file descriptor to be polled by the looper. + * If the same file descriptor was previously added, it is replaced. * * "fd" is the file descriptor to be added. - * "ident" is an identifier for this event, which is returned from - * ALooper_pollOnce(). Must be >= 0, or ALOOPER_POLL_CALLBACK if - * providing a non-NULL callback. - * "events" are the poll events to wake up on. Typically this is POLLIN. - * "callback" is the function to call when there is an event on the file - * descriptor. + * "ident" is an identifier for this event, which is returned from ALooper_pollOnce(). + * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. + * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. + * "callback" is the function to call when there is an event on the file descriptor. * "data" is a private data pointer to supply to the callback. * * There are two main uses of this function: * - * (1) If "callback" is non-NULL, then - * this function will be called when there is data on the file descriptor. It - * should execute any events it has pending, appropriately reading from the - * file descriptor. The 'ident' is ignored in this case. + * (1) If "callback" is non-NULL, then this function will be called when there is + * data on the file descriptor. It should execute any events it has pending, + * appropriately reading from the file descriptor. The 'ident' is ignored in this case. * * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce * when its file descriptor has data available, requiring the caller to take * care of processing it. + * + * Returns 1 if the file descriptor was added or -1 if an error occurred. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. */ -void ALooper_addFd(ALooper* looper, int fd, int ident, int events, - ALooper_callbackFunc* callback, void* data); +int ALooper_addFd(ALooper* looper, int fd, int ident, int events, + ALooper_callbackFunc callback, void* data); /** - * Remove a previously added file descriptor from the looper. + * Removes a previously added file descriptor from the looper. + * + * When this method returns, it is safe to close the file descriptor since the looper + * will no longer have a reference to it. However, it is possible for the callback to + * already be running or for it to run one last time if the file descriptor was already + * signalled. Calling code is responsible for ensuring that this case is safely handled. + * For example, if the callback takes care of removing itself during its own execution either + * by returning 0 or by calling this method, then it can be guaranteed to not be invoked + * again at any later time unless registered anew. + * + * Returns 1 if the file descriptor was removed, 0 if none was previously registered + * or -1 if an error occurred. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. */ -int32_t ALooper_removeFd(ALooper* looper, int fd); +int ALooper_removeFd(ALooper* looper, int fd); #ifdef __cplusplus }; diff --git a/native/include/android/sensor.h b/native/include/android/sensor.h index a102d43..f163f18 100644 --- a/native/include/android/sensor.h +++ b/native/include/android/sensor.h @@ -166,7 +166,7 @@ ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type * Creates a new sensor event queue and associate it with a looper. */ ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager, - ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data); + ALooper* looper, int ident, ALooper_callbackFunc callback, void* data); /* * Destroys the event queue and free all resources associated to it. diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 3025f77..e204e04 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -529,7 +529,7 @@ status_t SensorService::SensorEventConnection::sendEvents( LOGE_IF(size<0, "dropping %d events on the floor (%s)", count, strerror(-size)); - return size < 0 ? size : NO_ERROR; + return size < 0 ? status_t(size) : status_t(NO_ERROR); } sp<SensorChannel> SensorService::SensorEventConnection::getSensorChannel() const diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp index e464713..42bf983 100644 --- a/services/sensorservice/tests/sensorservicetest.cpp +++ b/services/sensorservice/tests/sensorservicetest.cpp @@ -18,11 +18,11 @@ #include <gui/Sensor.h> #include <gui/SensorManager.h> #include <gui/SensorEventQueue.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> using namespace android; -bool receiver(int fd, int events, void* data) +int receiver(int fd, int events, void* data) { sp<SensorEventQueue> q((SensorEventQueue*)data); ssize_t n; @@ -41,7 +41,7 @@ bool receiver(int fd, int events, void* data) if (n<0 && n != -EAGAIN) { printf("error reading events (%s)\n", strerror(-n)); } - return true; + return 1; } @@ -51,7 +51,7 @@ int main(int argc, char** argv) Sensor const* const* list; ssize_t count = mgr.getSensorList(&list); - printf("numSensors=%d\n", count); + printf("numSensors=%d\n", int(count)); sp<SensorEventQueue> q = mgr.createEventQueue(); printf("queue=%p\n", q.get()); @@ -63,13 +63,16 @@ int main(int argc, char** argv) q->setEventRate(accelerometer, ms2ns(10)); - sp<PollLoop> loop = new PollLoop(false); - loop->setCallback(q->getFd(), POLLIN, receiver, q.get()); + sp<Looper> loop = new Looper(false); + loop->addFd(q->getFd(), 0, ALOOPER_EVENT_INPUT, receiver, q.get()); do { //printf("about to poll...\n"); - int32_t ret = loop->pollOnce(-1, 0, 0); + int32_t ret = loop->pollOnce(-1); switch (ret) { + case ALOOPER_POLL_WAKE: + //("ALOOPER_POLL_WAKE\n"); + break; case ALOOPER_POLL_CALLBACK: //("ALOOPER_POLL_CALLBACK\n"); break; |