Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to be used by default for now. To enable native input dispatch, edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy. Includes part of the new input event NDK API. Some details TBD. To wire up input dispatch, as the ViewRoot adds a window to the window session it receives an InputChannel object as an output argument. The InputChannel encapsulates the file descriptors for a shared memory region and two pipe end-points. The ViewRoot then provides the InputChannel to the InputQueue. Behind the scenes, InputQueue simply attaches handlers to the native PollLoop object that underlies the MessageQueue. This way MessageQueue doesn't need to know anything about input dispatch per-se, it just exposes (in native code) a PollLoop that other components can use to monitor file descriptor state changes. There can be zero or more targets for any given input event. Each input target is specified by its input channel and some parameters including flags, an X/Y coordinate offset, and the dispatch timeout. An input target can request either synchronous dispatch (for foreground apps) or asynchronous dispatch (fire-and-forget for wallpapers and "outside" targets). Currently, finding the appropriate input targets for an event requires a call back into the WindowManagerServer from native code. In the future this will be refactored to avoid most of these callbacks except as required to handle pending focus transitions. End-to-end event dispatch mostly works! To do: event injection, rate limiting, ANRs, testing, optimization, etc. Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
This commit is contained in:
parent
10e5da58e4
commit
66db68948c
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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_BITSET_H
|
||||
#define UTILS_BITSET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Contains some bit manipulation helpers.
|
||||
*/
|
||||
|
||||
namespace android {
|
||||
|
||||
// A simple set of 32 bits that can be individually marked or cleared.
|
||||
struct BitSet32 {
|
||||
uint32_t value;
|
||||
|
||||
inline BitSet32() : value(0) { }
|
||||
explicit inline BitSet32(uint32_t value) : value(value) { }
|
||||
|
||||
// Gets the value associated with a particular bit index.
|
||||
static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; }
|
||||
|
||||
// Clears the bit set.
|
||||
inline void clear() { value = 0; }
|
||||
|
||||
// Returns true if the bit set does not contain any marked bits.
|
||||
inline bool isEmpty() const { return ! value; }
|
||||
|
||||
// Returns true if the specified bit is marked.
|
||||
inline bool hasBit(uint32_t n) const { return value & valueForBit(n); }
|
||||
|
||||
// Marks the specified bit.
|
||||
inline void markBit(uint32_t n) { value |= valueForBit(n); }
|
||||
|
||||
// Clears the specified bit.
|
||||
inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); }
|
||||
|
||||
// Finds the first marked bit in the set.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t firstMarkedBit() const { return __builtin_clz(value); }
|
||||
|
||||
// Finds the first unmarked bit in the set.
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
|
||||
|
||||
inline bool operator== (const BitSet32& other) const { return value == other.value; }
|
||||
inline bool operator!= (const BitSet32& other) const { return value != other.value; }
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // UTILS_BITSET_H
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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_BUFFER_H__
|
||||
#define __UTILS_BUFFER_H__ 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class Buffer
|
||||
{
|
||||
private:
|
||||
char *buf;
|
||||
int bufsiz;
|
||||
int used;
|
||||
void ensureCapacity(int len);
|
||||
|
||||
void
|
||||
makeRoomFor(int len)
|
||||
{
|
||||
if (len + used >= bufsiz) {
|
||||
bufsiz = (len + used) * 3/2 + 2;
|
||||
char *blah = new char[bufsiz];
|
||||
|
||||
memcpy(blah, buf, used);
|
||||
delete[] buf;
|
||||
buf = blah;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Buffer()
|
||||
{
|
||||
bufsiz = 16;
|
||||
buf = new char[bufsiz];
|
||||
clear();
|
||||
}
|
||||
|
||||
~Buffer()
|
||||
{
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
void
|
||||
clear()
|
||||
{
|
||||
buf[0] = '\0';
|
||||
used = 0;
|
||||
}
|
||||
|
||||
int
|
||||
length()
|
||||
{
|
||||
return used;
|
||||
}
|
||||
|
||||
void
|
||||
append(const char c)
|
||||
{
|
||||
makeRoomFor(1);
|
||||
buf[used] = c;
|
||||
used++;
|
||||
buf[used] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
append(const char *s, int len)
|
||||
{
|
||||
makeRoomFor(len);
|
||||
|
||||
memcpy(buf + used, s, len);
|
||||
used += len;
|
||||
buf[used] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
append(const char *s)
|
||||
{
|
||||
append(s, strlen(s));
|
||||
}
|
||||
|
||||
char *
|
||||
getBytes()
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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 <poll.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* A basic file descriptor polling loop based on poll() with callbacks.
|
||||
*/
|
||||
class PollLoop : public RefBase {
|
||||
protected:
|
||||
virtual ~PollLoop();
|
||||
|
||||
public:
|
||||
PollLoop();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 true if a callback was invoked or if the loop was awoken by wake().
|
||||
* Returns false if a timeout or error occurred.
|
||||
*
|
||||
* This method must only be called on the main thread.
|
||||
* 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.
|
||||
*/
|
||||
bool pollOnce(int timeoutMillis);
|
||||
|
||||
/**
|
||||
* Wakes the loop asynchronously.
|
||||
*
|
||||
* This method can be called on any thread.
|
||||
* This method returns immediately.
|
||||
*/
|
||||
void wake();
|
||||
|
||||
/**
|
||||
* 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 events, Callback callback, void* data = NULL);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
private:
|
||||
struct RequestedCallback {
|
||||
Callback callback;
|
||||
void* data;
|
||||
};
|
||||
|
||||
struct PendingCallback {
|
||||
int fd;
|
||||
int events;
|
||||
Callback callback;
|
||||
void* data;
|
||||
};
|
||||
|
||||
Mutex mLock;
|
||||
Condition mAwake;
|
||||
bool mPolling;
|
||||
|
||||
int mWakeReadPipeFd;
|
||||
int mWakeWritePipeFd;
|
||||
|
||||
Vector<struct pollfd> mRequestedFds;
|
||||
Vector<RequestedCallback> mRequestedCallbacks;
|
||||
|
||||
Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce
|
||||
|
||||
void openWakePipe();
|
||||
void closeWakePipe();
|
||||
|
||||
ssize_t getRequestIndexLocked(int fd);
|
||||
void wakeAndLock();
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // UTILS_POLL_LOOP_H
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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_POOL_H
|
||||
#define UTILS_POOL_H
|
||||
|
||||
#include <utils/TypeHelpers.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class PoolImpl {
|
||||
public:
|
||||
PoolImpl(size_t objSize);
|
||||
~PoolImpl();
|
||||
|
||||
void* allocImpl();
|
||||
void freeImpl(void* obj);
|
||||
|
||||
private:
|
||||
size_t mObjSize;
|
||||
};
|
||||
|
||||
/*
|
||||
* A homogeneous typed memory pool for fixed size objects.
|
||||
* Not intended to be thread-safe.
|
||||
*/
|
||||
template<typename T>
|
||||
class Pool : private PoolImpl {
|
||||
public:
|
||||
/* Creates an initially empty pool. */
|
||||
Pool() : PoolImpl(sizeof(T)) { }
|
||||
|
||||
/* Destroys the pool.
|
||||
* Assumes that the pool is empty. */
|
||||
~Pool() { }
|
||||
|
||||
/* Allocates an object from the pool, growing the pool if needed. */
|
||||
inline T* alloc() {
|
||||
void* mem = allocImpl();
|
||||
if (! traits<T>::has_trivial_ctor) {
|
||||
return new (mem) T();
|
||||
} else {
|
||||
return static_cast<T*>(mem);
|
||||
}
|
||||
}
|
||||
|
||||
/* Frees an object from the pool. */
|
||||
inline void free(T* obj) {
|
||||
if (! traits<T>::has_trivial_dtor) {
|
||||
obj->~T();
|
||||
}
|
||||
freeImpl(obj);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // UTILS_POOL_H
|
|
@ -37,6 +37,8 @@ public:
|
|||
const char* name() const;
|
||||
nsecs_t lap();
|
||||
nsecs_t elapsedTime() const;
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
const char* mName;
|
||||
|
|
|
@ -114,6 +114,12 @@ public:
|
|||
ssize_t appendVector(const Vector<TYPE>& vector);
|
||||
|
||||
|
||||
//! insert an array at a given index
|
||||
ssize_t insertArrayAt(const TYPE* array, size_t index, size_t numItems);
|
||||
|
||||
//! append an array at the end of this vector
|
||||
ssize_t appendArray(const TYPE* array, size_t numItems);
|
||||
|
||||
/*!
|
||||
* add/insert/replace items
|
||||
*/
|
||||
|
@ -258,6 +264,16 @@ ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
|
|||
return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
|
||||
}
|
||||
|
||||
template<class TYPE> inline
|
||||
ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t numItems) {
|
||||
return VectorImpl::insertAt(array, index, numItems);
|
||||
}
|
||||
|
||||
template<class TYPE> inline
|
||||
ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t numItems) {
|
||||
return VectorImpl::add(array, numItems);
|
||||
}
|
||||
|
||||
template<class TYPE> inline
|
||||
ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
|
||||
return VectorImpl::insertAt(&item, index, numItems);
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
void push();
|
||||
void push(const void* item);
|
||||
ssize_t add();
|
||||
ssize_t add(const void* item);
|
||||
ssize_t add(const void* item, size_t numItems = 1);
|
||||
ssize_t replaceAt(size_t index);
|
||||
ssize_t replaceAt(const void* item, size_t index);
|
||||
|
||||
|
@ -184,6 +184,8 @@ private:
|
|||
void push(const void* item);
|
||||
ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
|
||||
ssize_t appendVector(const VectorImpl& vector);
|
||||
ssize_t insertArrayAt(const void* array, size_t index, size_t numItems);
|
||||
ssize_t appendArray(const void* array, size_t numItems);
|
||||
ssize_t insertAt(size_t where, size_t numItems = 1);
|
||||
ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
|
||||
ssize_t replaceAt(size_t index);
|
||||
|
|
|
@ -26,6 +26,8 @@ commonSources:= \
|
|||
Debug.cpp \
|
||||
FileMap.cpp \
|
||||
Flattenable.cpp \
|
||||
PollLoop.cpp \
|
||||
Pool.cpp \
|
||||
RefBase.cpp \
|
||||
ResourceTypes.cpp \
|
||||
SharedBuffer.cpp \
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
//
|
||||
// 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 1
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <utils/PollLoop.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
PollLoop::PollLoop() :
|
||||
mPolling(false) {
|
||||
openWakePipe();
|
||||
}
|
||||
|
||||
PollLoop::~PollLoop() {
|
||||
closeWakePipe();
|
||||
}
|
||||
|
||||
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.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.
|
||||
}
|
||||
|
||||
bool PollLoop::pollOnce(int timeoutMillis) {
|
||||
mLock.lock();
|
||||
mPolling = true;
|
||||
mLock.unlock();
|
||||
|
||||
bool 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 = false;
|
||||
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 = false;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
#if DEBUG_POLL_AND_WAKE
|
||||
LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount);
|
||||
for (size_t i = 0; i < requestedCount; i++) {
|
||||
LOGD(" fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events,
|
||||
mRequestedFds[i].revents);
|
||||
}
|
||||
#endif
|
||||
|
||||
mPendingCallbacks.clear();
|
||||
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);
|
||||
Callback callback = requestedCallback.callback;
|
||||
|
||||
if (callback) {
|
||||
PendingCallback pendingCallback;
|
||||
pendingCallback.fd = requestedFd.fd;
|
||||
pendingCallback.events = requestedFd.revents;
|
||||
pendingCallback.callback = callback;
|
||||
pendingCallback.data = requestedCallback.data;
|
||||
mPendingCallbacks.push(pendingCallback);
|
||||
} else {
|
||||
if (requestedFd.fd == mWakeReadPipeFd) {
|
||||
#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 {
|
||||
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
|
||||
LOGD("%p ~ pollOnce - fd %d has no callback!", this, requestedFd.fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
respondedCount -= 1;
|
||||
if (respondedCount == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
|
||||
Done:
|
||||
mLock.lock();
|
||||
mPolling = false;
|
||||
mAwake.broadcast();
|
||||
mLock.unlock();
|
||||
|
||||
if (result) {
|
||||
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 = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
|
||||
pendingCallback.data);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
|
||||
#if DEBUG_CALLBACKS
|
||||
LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events);
|
||||
#endif
|
||||
|
||||
if (! events || ! callback) {
|
||||
LOGE("Invalid attempt to set a callback with no selected poll events or no callback.");
|
||||
removeCallback(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
wakeAndLock();
|
||||
|
||||
struct pollfd requestedFd;
|
||||
requestedFd.fd = fd;
|
||||
requestedFd.events = events;
|
||||
|
||||
RequestedCallback requestedCallback;
|
||||
requestedCallback.callback = callback;
|
||||
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();
|
||||
while (mPolling) {
|
||||
wake();
|
||||
mAwake.wait(mLock);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// Copyright 2010 The Android Open Source Project
|
||||
//
|
||||
// A simple memory pool.
|
||||
//
|
||||
#define LOG_TAG "Pool"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <utils/Pool.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// TODO Provide a real implementation of a pool. This is just a stub for initial development.
|
||||
|
||||
PoolImpl::PoolImpl(size_t objSize) :
|
||||
mObjSize(objSize) {
|
||||
}
|
||||
|
||||
PoolImpl::~PoolImpl() {
|
||||
}
|
||||
|
||||
void* PoolImpl::allocImpl() {
|
||||
void* ptr = malloc(mObjSize);
|
||||
LOG_ALWAYS_FATAL_IF(ptr == NULL, "Cannot allocate new pool object.");
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void PoolImpl::freeImpl(void* obj) {
|
||||
LOG_ALWAYS_FATAL_IF(obj == NULL, "Caller attempted to free NULL pool object.");
|
||||
return free(obj);
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -30,10 +30,9 @@ namespace android {
|
|||
|
||||
|
||||
StopWatch::StopWatch(const char *name, int clock, uint32_t flags)
|
||||
: mName(name), mClock(clock), mFlags(flags),
|
||||
mStartTime(0), mNumLaps(0)
|
||||
: mName(name), mClock(clock), mFlags(flags)
|
||||
{
|
||||
mStartTime = systemTime(mClock);
|
||||
reset();
|
||||
}
|
||||
|
||||
StopWatch::~StopWatch()
|
||||
|
@ -72,6 +71,12 @@ nsecs_t StopWatch::elapsedTime() const
|
|||
return systemTime(mClock) - mStartTime;
|
||||
}
|
||||
|
||||
void StopWatch::reset()
|
||||
{
|
||||
mNumLaps = 0;
|
||||
mStartTime = systemTime(mClock);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
@ -108,13 +108,7 @@ size_t VectorImpl::capacity() const
|
|||
|
||||
ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
|
||||
{
|
||||
if (index > size())
|
||||
return BAD_INDEX;
|
||||
void* where = _grow(index, vector.size());
|
||||
if (where) {
|
||||
_do_copy(where, vector.arrayImpl(), vector.size());
|
||||
}
|
||||
return where ? index : (ssize_t)NO_MEMORY;
|
||||
return insertAt(vector.arrayImpl(), index, vector.size());
|
||||
}
|
||||
|
||||
ssize_t VectorImpl::appendVector(const VectorImpl& vector)
|
||||
|
@ -226,9 +220,9 @@ ssize_t VectorImpl::add()
|
|||
return add(0);
|
||||
}
|
||||
|
||||
ssize_t VectorImpl::add(const void* item)
|
||||
ssize_t VectorImpl::add(const void* item, size_t numItems)
|
||||
{
|
||||
return insertAt(item, size());
|
||||
return insertAt(item, size(), numItems);
|
||||
}
|
||||
|
||||
ssize_t VectorImpl::replaceAt(size_t index)
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Build the unit tests.
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
test_src_files := \
|
||||
PollLoop_test.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libz \
|
||||
liblog \
|
||||
libcutils \
|
||||
libutils \
|
||||
libstlport
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libgtest \
|
||||
libgtest_main
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
external/zlib \
|
||||
external/icu4c/common \
|
||||
bionic \
|
||||
bionic/libstdc++/include \
|
||||
external/gtest/include \
|
||||
external/stlport/stlport
|
||||
|
||||
LOCAL_MODULE_TAGS := eng tests
|
||||
|
||||
$(foreach file,$(test_src_files), \
|
||||
$(eval LOCAL_SRC_FILES := $(file)) \
|
||||
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
|
||||
$(eval include $(BUILD_EXECUTABLE)) \
|
||||
)
|
|
@ -0,0 +1,398 @@
|
|||
//
|
||||
// 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 Pipe {
|
||||
public:
|
||||
int sendFd;
|
||||
int receiveFd;
|
||||
|
||||
Pipe() {
|
||||
int fds[2];
|
||||
::pipe(fds);
|
||||
|
||||
receiveFd = fds[0];
|
||||
sendFd = fds[1];
|
||||
}
|
||||
|
||||
~Pipe() {
|
||||
::close(sendFd);
|
||||
::close(receiveFd);
|
||||
}
|
||||
|
||||
bool writeSignal() {
|
||||
return ::write(sendFd, "*", 1) == 1;
|
||||
}
|
||||
|
||||
bool readSignal() {
|
||||
char buf[1];
|
||||
return ::read(receiveFd, buf, 1) == 1;
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
mPollLoop.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) {
|
||||
StopWatch stopWatch("pollOnce");
|
||||
bool result = mPollLoop->pollOnce(100);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
|
||||
<< "elapsed time should approx. equal timeout";
|
||||
EXPECT_FALSE(result)
|
||||
<< "pollOnce result should be false because timeout occurred";
|
||||
}
|
||||
|
||||
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) {
|
||||
mPollLoop->wake();
|
||||
|
||||
StopWatch stopWatch("pollOnce");
|
||||
bool 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_TRUE(result)
|
||||
<< "pollOnce result should be true because loop was awoken";
|
||||
}
|
||||
|
||||
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) {
|
||||
sp<DelayedWake> delayedWake = new DelayedWake(100, mPollLoop);
|
||||
delayedWake->run();
|
||||
|
||||
StopWatch stopWatch("pollOnce");
|
||||
bool 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_TRUE(result)
|
||||
<< "pollOnce result should be true because loop was awoken";
|
||||
}
|
||||
|
||||
TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) {
|
||||
StopWatch stopWatch("pollOnce");
|
||||
bool result = mPollLoop->pollOnce(0);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
|
||||
<< "elapsed time should be approx. zero";
|
||||
EXPECT_FALSE(result)
|
||||
<< "pollOnce result should be false because timeout occurred";
|
||||
}
|
||||
|
||||
TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) {
|
||||
Pipe pipe;
|
||||
StubCallbackHandler handler(true);
|
||||
|
||||
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
|
||||
|
||||
StopWatch stopWatch("pollOnce");
|
||||
bool result = mPollLoop->pollOnce(0);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
|
||||
<< "elapsed time should be approx. zero";
|
||||
EXPECT_FALSE(result)
|
||||
<< "pollOnce result should be false because timeout occurred";
|
||||
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_TRUE(pipe.writeSignal());
|
||||
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
|
||||
|
||||
StopWatch stopWatch("pollOnce");
|
||||
bool result = mPollLoop->pollOnce(0);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
|
||||
<< "elapsed time should be approx. zero";
|
||||
EXPECT_TRUE(result)
|
||||
<< "pollOnce result should be true 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");
|
||||
bool result = mPollLoop->pollOnce(100);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
|
||||
<< "elapsed time should approx. equal timeout";
|
||||
EXPECT_FALSE(result)
|
||||
<< "pollOnce result should be false because timeout occurred";
|
||||
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");
|
||||
bool result = mPollLoop->pollOnce(100);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
ASSERT_TRUE(pipe.readSignal())
|
||||
<< "signal should actually have been written";
|
||||
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
|
||||
<< "elapsed time should be approx. zero";
|
||||
EXPECT_TRUE(result)
|
||||
<< "pollOnce result should be true 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");
|
||||
bool result = mPollLoop->pollOnce(1000);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
ASSERT_TRUE(pipe.readSignal())
|
||||
<< "signal should actually have been written";
|
||||
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
|
||||
<< "elapsed time should approx. equal signal delay";
|
||||
EXPECT_TRUE(result)
|
||||
<< "pollOnce result should be true 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");
|
||||
bool result = mPollLoop->pollOnce(100);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
ASSERT_TRUE(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_FALSE(result)
|
||||
<< "pollOnce result should be false because timeout occurred";
|
||||
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");
|
||||
bool result = mPollLoop->pollOnce(0);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
ASSERT_TRUE(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_TRUE(result)
|
||||
<< "pollOnce result should be true 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_TRUE(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_FALSE(result)
|
||||
<< "pollOnce result should be false because timeout occurred";
|
||||
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");
|
||||
bool result = mPollLoop->pollOnce(100);
|
||||
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
|
||||
|
||||
ASSERT_TRUE(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_TRUE(result)
|
||||
<< "pollOnce result should be true 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
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 TESTHELPERS_H
|
||||
#define TESTHELPERS_H
|
||||
|
||||
#include <utils/threads.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class DelayedTask : public Thread {
|
||||
int mDelayMillis;
|
||||
|
||||
public:
|
||||
DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
|
||||
|
||||
protected:
|
||||
virtual ~DelayedTask() { }
|
||||
|
||||
virtual void doTask() = 0;
|
||||
|
||||
virtual bool threadLoop() {
|
||||
usleep(mDelayMillis * 1000);
|
||||
doTask();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // TESTHELPERS_H
|
Loading…
Reference in New Issue