am 09340a4b: Merge "Replace epoll() with poll() and rename PollLoop to Looper." into gingerbread
Merge commit '09340a4bb99507d9b6bfbfc68a450d4d4f354d73' into gingerbread-plus-aosp * commit '09340a4bb99507d9b6bfbfc68a450d4d4f354d73': Replace epoll() with poll() and rename PollLoop to Looper.
This commit is contained in:
commit
4f5d8bf9a1
@ -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;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
|
210
include/utils/Looper.h
Normal file
210
include/utils/Looper.h
Normal file
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ LOCAL_SRC_FILES:= \
|
||||
$(commonSources) \
|
||||
BackupData.cpp \
|
||||
BackupHelpers.cpp \
|
||||
PollLoop.cpp
|
||||
Looper.cpp
|
||||
|
||||
ifeq ($(TARGET_OS),linux)
|
||||
LOCAL_LDLIBS += -lrt -ldl
|
||||
|
368
libs/utils/Looper.cpp
Normal file
368
libs/utils/Looper.cpp
Normal file
@ -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
|
@ -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
|
@ -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 := \
|
||||
|
433
libs/utils/tests/Looper_test.cpp
Normal file
433
libs/utils/tests/Looper_test.cpp
Normal file
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user