am 14bc6b5d: am 09340a4b: Merge "Replace epoll() with poll() and rename PollLoop to Looper." into gingerbread
Merge commit '14bc6b5d0677e5c454a67775c852f90389bb4567' * commit '14bc6b5d0677e5c454a67775c852f90389bb4567': Replace epoll() with poll() and rename PollLoop to Looper.
This commit is contained in:
commit
dc543aec70
@ -42,7 +42,7 @@ namespace android {
|
|||||||
|
|
||||||
class ISensorEventConnection;
|
class ISensorEventConnection;
|
||||||
class Sensor;
|
class Sensor;
|
||||||
class PollLoop;
|
class Looper;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -69,11 +69,11 @@ public:
|
|||||||
status_t disableSensor(int32_t handle) const;
|
status_t disableSensor(int32_t handle) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sp<PollLoop> getPollLoop() const;
|
sp<Looper> getLooper() const;
|
||||||
sp<ISensorEventConnection> mSensorEventConnection;
|
sp<ISensorEventConnection> mSensorEventConnection;
|
||||||
sp<SensorChannel> mSensorChannel;
|
sp<SensorChannel> mSensorChannel;
|
||||||
mutable Mutex mLock;
|
mutable Mutex mLock;
|
||||||
mutable sp<PollLoop> mPollLoop;
|
mutable sp<Looper> mLooper;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include <utils/Timers.h>
|
#include <utils/Timers.h>
|
||||||
#include <utils/RefBase.h>
|
#include <utils/RefBase.h>
|
||||||
#include <utils/String8.h>
|
#include <utils/String8.h>
|
||||||
#include <utils/PollLoop.h>
|
#include <utils/Looper.h>
|
||||||
#include <utils/Pool.h>
|
#include <utils/Pool.h>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
@ -826,7 +826,7 @@ private:
|
|||||||
Mutex mLock;
|
Mutex mLock;
|
||||||
|
|
||||||
Allocator mAllocator;
|
Allocator mAllocator;
|
||||||
sp<PollLoop> mPollLoop;
|
sp<Looper> mLooper;
|
||||||
|
|
||||||
EventEntry* mPendingEvent;
|
EventEntry* mPendingEvent;
|
||||||
Queue<EventEntry> mInboundQueue;
|
Queue<EventEntry> mInboundQueue;
|
||||||
@ -837,7 +837,7 @@ private:
|
|||||||
void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
|
void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
|
||||||
nsecs_t* nextWakeupTime);
|
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);
|
bool enqueueInboundEventLocked(EventEntry* entry);
|
||||||
|
|
||||||
// App switch latency optimization.
|
// App switch latency optimization.
|
||||||
@ -1010,7 +1010,7 @@ private:
|
|||||||
void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||||
bool broken);
|
bool broken);
|
||||||
void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain);
|
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.
|
// Preempting input dispatch.
|
||||||
bool preemptInputDispatchInnerLocked();
|
bool preemptInputDispatchInnerLocked();
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <ui/Input.h>
|
#include <ui/Input.h>
|
||||||
#include <utils/Errors.h>
|
#include <utils/Errors.h>
|
||||||
#include <utils/PollLoop.h>
|
|
||||||
#include <utils/Timers.h>
|
#include <utils/Timers.h>
|
||||||
#include <utils/RefBase.h>
|
#include <utils/RefBase.h>
|
||||||
#include <utils/String8.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/Errors.h>
|
||||||
#include <utils/RefBase.h>
|
#include <utils/RefBase.h>
|
||||||
#include <utils/PollLoop.h>
|
#include <utils/Looper.h>
|
||||||
|
|
||||||
#include <gui/Sensor.h>
|
#include <gui/Sensor.h>
|
||||||
#include <gui/SensorChannel.h>
|
#include <gui/SensorChannel.h>
|
||||||
@ -81,28 +81,28 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<PollLoop> SensorEventQueue::getPollLoop() const
|
sp<Looper> SensorEventQueue::getLooper() const
|
||||||
{
|
{
|
||||||
Mutex::Autolock _l(mLock);
|
Mutex::Autolock _l(mLock);
|
||||||
if (mPollLoop == 0) {
|
if (mLooper == 0) {
|
||||||
mPollLoop = new PollLoop(true);
|
mLooper = new Looper(true);
|
||||||
mPollLoop->setCallback(getFd(), getFd(), POLLIN, NULL, NULL);
|
mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
|
||||||
}
|
}
|
||||||
return mPollLoop;
|
return mLooper;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t SensorEventQueue::waitForEvent() const
|
status_t SensorEventQueue::waitForEvent() const
|
||||||
{
|
{
|
||||||
const int fd = getFd();
|
const int fd = getFd();
|
||||||
sp<PollLoop> pollLoop(getPollLoop());
|
sp<Looper> looper(getLooper());
|
||||||
int32_t result = pollLoop->pollOnce(-1, NULL, NULL);
|
int32_t result = looper->pollOnce(-1);
|
||||||
return (result == fd) ? NO_ERROR : -1;
|
return (result == fd) ? status_t(NO_ERROR) : status_t(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t SensorEventQueue::wake() const
|
status_t SensorEventQueue::wake() const
|
||||||
{
|
{
|
||||||
sp<PollLoop> pollLoop(getPollLoop());
|
sp<Looper> looper(getLooper());
|
||||||
pollLoop->wake();
|
looper->wake();
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
|
|||||||
mFocusedApplication(NULL),
|
mFocusedApplication(NULL),
|
||||||
mCurrentInputTargetsValid(false),
|
mCurrentInputTargetsValid(false),
|
||||||
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
|
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
|
||||||
mPollLoop = new PollLoop(false);
|
mLooper = new Looper(false);
|
||||||
|
|
||||||
mInboundQueue.headSentinel.refCount = -1;
|
mInboundQueue.headSentinel.refCount = -1;
|
||||||
mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
|
mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
|
||||||
@ -156,7 +156,7 @@ void InputDispatcher::dispatchOnce() {
|
|||||||
timeoutMillis = 0;
|
timeoutMillis = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mPollLoop->pollOnce(timeoutMillis);
|
mLooper->pollOnce(timeoutMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
|
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);
|
InputDispatcher* d = static_cast<InputDispatcher*>(data);
|
||||||
|
|
||||||
{ // acquire lock
|
{ // acquire lock
|
||||||
@ -1794,24 +1794,24 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat
|
|||||||
if (connectionIndex < 0) {
|
if (connectionIndex < 0) {
|
||||||
LOGE("Received spurious receive callback for unknown input channel. "
|
LOGE("Received spurious receive callback for unknown input channel. "
|
||||||
"fd=%d, events=0x%x", receiveFd, events);
|
"fd=%d, events=0x%x", receiveFd, events);
|
||||||
return false; // remove the callback
|
return 0; // remove the callback
|
||||||
}
|
}
|
||||||
|
|
||||||
nsecs_t currentTime = now();
|
nsecs_t currentTime = now();
|
||||||
|
|
||||||
sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
|
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. "
|
LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. "
|
||||||
"events=0x%x", connection->getInputChannelName(), events);
|
"events=0x%x", connection->getInputChannelName(), events);
|
||||||
d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
||||||
d->runCommandsLockedInterruptible();
|
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. "
|
LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
|
||||||
"events=0x%x", connection->getInputChannelName(), events);
|
"events=0x%x", connection->getInputChannelName(), events);
|
||||||
return true;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t status = connection->inputPublisher.receiveFinishedSignal();
|
status_t status = connection->inputPublisher.receiveFinishedSignal();
|
||||||
@ -1820,12 +1820,12 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat
|
|||||||
connection->getInputChannelName(), status);
|
connection->getInputChannelName(), status);
|
||||||
d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
||||||
d->runCommandsLockedInterruptible();
|
d->runCommandsLockedInterruptible();
|
||||||
return false; // remove the callback
|
return 0; // remove the callback
|
||||||
}
|
}
|
||||||
|
|
||||||
d->finishDispatchCycleLocked(currentTime, connection);
|
d->finishDispatchCycleLocked(currentTime, connection);
|
||||||
d->runCommandsLockedInterruptible();
|
d->runCommandsLockedInterruptible();
|
||||||
return true;
|
return 1;
|
||||||
} // release lock
|
} // release lock
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1843,7 +1843,7 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
|
|||||||
} // release lock
|
} // release lock
|
||||||
|
|
||||||
if (needWake) {
|
if (needWake) {
|
||||||
mPollLoop->wake();
|
mLooper->wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1870,7 +1870,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou
|
|||||||
} // release lock
|
} // release lock
|
||||||
|
|
||||||
if (needWake) {
|
if (needWake) {
|
||||||
mPollLoop->wake();
|
mLooper->wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2007,7 +2007,7 @@ NoBatchingOrStreaming:;
|
|||||||
} // release lock
|
} // release lock
|
||||||
|
|
||||||
if (needWake) {
|
if (needWake) {
|
||||||
mPollLoop->wake();
|
mLooper->wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2043,7 +2043,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
|
|||||||
} // release lock
|
} // release lock
|
||||||
|
|
||||||
if (needWake) {
|
if (needWake) {
|
||||||
mPollLoop->wake();
|
mLooper->wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t injectionResult;
|
int32_t injectionResult;
|
||||||
@ -2294,7 +2294,7 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
|
|||||||
} // release lock
|
} // release lock
|
||||||
|
|
||||||
// Wake up poll loop since it may need to make new input dispatching choices.
|
// Wake up poll loop since it may need to make new input dispatching choices.
|
||||||
mPollLoop->wake();
|
mLooper->wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
|
void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
|
||||||
@ -2317,7 +2317,7 @@ void InputDispatcher::setFocusedApplication(const InputApplication* inputApplica
|
|||||||
} // release lock
|
} // release lock
|
||||||
|
|
||||||
// Wake up poll loop since it may need to make new input dispatching choices.
|
// Wake up poll loop since it may need to make new input dispatching choices.
|
||||||
mPollLoop->wake();
|
mLooper->wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputDispatcher::releaseFocusedApplicationLocked() {
|
void InputDispatcher::releaseFocusedApplicationLocked() {
|
||||||
@ -2355,7 +2355,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
|
|||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
// Wake up poll loop since it may need to make new input dispatching choices.
|
// 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) {
|
if (preemptedOne) {
|
||||||
// Wake up the poll loop so it can get a head start dispatching the next event.
|
// 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);
|
mMonitoringChannels.push(inputChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
|
mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
|
||||||
|
|
||||||
runCommandsLockedInterruptible();
|
runCommandsLockedInterruptible();
|
||||||
} // release lock
|
} // 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();
|
nsecs_t currentTime = now();
|
||||||
abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
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
|
// Wake the poll loop because removing the connection may have changed the current
|
||||||
// synchronization state.
|
// synchronization state.
|
||||||
mPollLoop->wake();
|
mLooper->wake();
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ LOCAL_SRC_FILES:= \
|
|||||||
$(commonSources) \
|
$(commonSources) \
|
||||||
BackupData.cpp \
|
BackupData.cpp \
|
||||||
BackupHelpers.cpp \
|
BackupHelpers.cpp \
|
||||||
PollLoop.cpp
|
Looper.cpp
|
||||||
|
|
||||||
ifeq ($(TARGET_OS),linux)
|
ifeq ($(TARGET_OS),linux)
|
||||||
LOCAL_LDLIBS += -lrt -ldl
|
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.
|
# Build the unit tests.
|
||||||
test_src_files := \
|
test_src_files := \
|
||||||
ObbFile_test.cpp \
|
ObbFile_test.cpp \
|
||||||
PollLoop_test.cpp \
|
Looper_test.cpp \
|
||||||
String8_test.cpp
|
String8_test.cpp
|
||||||
|
|
||||||
shared_libraries := \
|
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