2010-09-14 08:17:30 +02:00
|
|
|
//
|
|
|
|
// 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.
|
2022-10-08 07:06:52 +02:00
|
|
|
#ifndef DEBUG_POLL_AND_WAKE
|
2010-09-14 08:17:30 +02:00
|
|
|
#define DEBUG_POLL_AND_WAKE 0
|
2022-10-08 07:06:52 +02:00
|
|
|
#endif
|
2010-09-14 08:17:30 +02:00
|
|
|
|
|
|
|
// Debugs callback registration and invocation.
|
2022-10-08 07:06:52 +02:00
|
|
|
#ifndef DEBUG_CALLBACKS
|
2010-09-14 08:17:30 +02:00
|
|
|
#define DEBUG_CALLBACKS 0
|
2022-10-08 07:06:52 +02:00
|
|
|
#endif
|
2010-09-14 08:17:30 +02:00
|
|
|
|
2016-09-28 19:07:20 +02:00
|
|
|
#include <utils/Looper.h>
|
2019-05-11 15:29:28 +02:00
|
|
|
|
2017-03-01 00:06:51 +01:00
|
|
|
#include <sys/eventfd.h>
|
2019-05-11 15:29:28 +02:00
|
|
|
#include <cinttypes>
|
2010-09-14 08:17:30 +02:00
|
|
|
|
|
|
|
namespace android {
|
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr uint64_t WAKE_EVENT_FD_SEQ = 1;
|
|
|
|
|
|
|
|
epoll_event createEpollEvent(uint32_t events, uint64_t seq) {
|
|
|
|
return {.events = events, .data = {.u64 = seq}};
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// --- WeakMessageHandler ---
|
|
|
|
|
|
|
|
WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
|
|
|
|
mHandler(handler) {
|
|
|
|
}
|
|
|
|
|
2012-06-01 01:15:35 +02:00
|
|
|
WeakMessageHandler::~WeakMessageHandler() {
|
|
|
|
}
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
void WeakMessageHandler::handleMessage(const Message& message) {
|
|
|
|
sp<MessageHandler> handler = mHandler.promote();
|
2018-07-17 03:11:34 +02:00
|
|
|
if (handler != nullptr) {
|
2011-03-02 23:41:58 +01:00
|
|
|
handler->handleMessage(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-01 01:15:35 +02:00
|
|
|
// --- SimpleLooperCallback ---
|
|
|
|
|
2013-12-12 07:46:45 +01:00
|
|
|
SimpleLooperCallback::SimpleLooperCallback(Looper_callbackFunc callback) :
|
2012-06-01 01:15:35 +02:00
|
|
|
mCallback(callback) {
|
|
|
|
}
|
|
|
|
|
|
|
|
SimpleLooperCallback::~SimpleLooperCallback() {
|
|
|
|
}
|
|
|
|
|
|
|
|
int SimpleLooperCallback::handleEvent(int fd, int events, void* data) {
|
|
|
|
return mCallback(fd, events, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// --- Looper ---
|
|
|
|
|
2010-09-14 08:17:30 +02:00
|
|
|
// Maximum number of file descriptors for which to retrieve poll events each iteration.
|
|
|
|
static const int EPOLL_MAX_EVENTS = 16;
|
|
|
|
|
2010-09-22 00:11:18 +02:00
|
|
|
static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
|
|
|
|
static pthread_key_t gTLSKey = 0;
|
|
|
|
|
2018-07-19 02:26:24 +02:00
|
|
|
Looper::Looper(bool allowNonCallbacks)
|
|
|
|
: mAllowNonCallbacks(allowNonCallbacks),
|
|
|
|
mSendingMessage(false),
|
|
|
|
mPolling(false),
|
|
|
|
mEpollRebuildRequired(false),
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
|
2018-07-19 02:26:24 +02:00
|
|
|
mResponseIndex(0),
|
|
|
|
mNextMessageUptime(LLONG_MAX) {
|
|
|
|
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
|
|
|
|
LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
|
2010-09-14 08:17:30 +02:00
|
|
|
|
2015-03-13 03:32:39 +01:00
|
|
|
AutoMutex _l(mLock);
|
|
|
|
rebuildEpollLocked();
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Looper::~Looper() {
|
|
|
|
}
|
|
|
|
|
2010-09-22 00:11:18 +02:00
|
|
|
void Looper::initTLSKey() {
|
2019-04-01 21:22:55 +02:00
|
|
|
int error = pthread_key_create(&gTLSKey, threadDestructor);
|
|
|
|
LOG_ALWAYS_FATAL_IF(error != 0, "Could not allocate TLS key: %s", strerror(error));
|
2010-09-22 00:11:18 +02:00
|
|
|
}
|
|
|
|
|
2010-09-14 08:17:30 +02:00
|
|
|
void Looper::threadDestructor(void *st) {
|
|
|
|
Looper* const self = static_cast<Looper*>(st);
|
2018-07-17 03:11:34 +02:00
|
|
|
if (self != nullptr) {
|
2010-09-14 08:17:30 +02:00
|
|
|
self->decStrong((void*)threadDestructor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Looper::setForThread(const sp<Looper>& looper) {
|
|
|
|
sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
|
|
|
|
|
2018-07-17 03:11:34 +02:00
|
|
|
if (looper != nullptr) {
|
2010-09-14 08:17:30 +02:00
|
|
|
looper->incStrong((void*)threadDestructor);
|
|
|
|
}
|
|
|
|
|
2010-09-22 00:11:18 +02:00
|
|
|
pthread_setspecific(gTLSKey, looper.get());
|
2010-09-14 08:17:30 +02:00
|
|
|
|
2018-07-17 03:11:34 +02:00
|
|
|
if (old != nullptr) {
|
2010-09-14 08:17:30 +02:00
|
|
|
old->decStrong((void*)threadDestructor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sp<Looper> Looper::getForThread() {
|
2010-09-22 00:11:18 +02:00
|
|
|
int result = pthread_once(& gTLSOnce, initTLSKey);
|
|
|
|
LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
|
2010-09-14 08:17:30 +02:00
|
|
|
|
2021-04-27 02:09:23 +02:00
|
|
|
Looper* looper = (Looper*)pthread_getspecific(gTLSKey);
|
|
|
|
return sp<Looper>::fromExisting(looper);
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sp<Looper> Looper::prepare(int opts) {
|
2013-12-12 07:46:45 +01:00
|
|
|
bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
|
2010-09-14 08:17:30 +02:00
|
|
|
sp<Looper> looper = Looper::getForThread();
|
2018-07-17 03:11:34 +02:00
|
|
|
if (looper == nullptr) {
|
2021-04-27 02:09:23 +02:00
|
|
|
looper = sp<Looper>::make(allowNonCallbacks);
|
2010-09-14 08:17:30 +02:00
|
|
|
Looper::setForThread(looper);
|
|
|
|
}
|
|
|
|
if (looper->getAllowNonCallbacks() != allowNonCallbacks) {
|
2012-01-06 00:22:43 +01:00
|
|
|
ALOGW("Looper already prepared for this thread with a different value for the "
|
2013-12-12 07:46:45 +01:00
|
|
|
"LOOPER_PREPARE_ALLOW_NON_CALLBACKS option.");
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
return looper;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Looper::getAllowNonCallbacks() const {
|
|
|
|
return mAllowNonCallbacks;
|
|
|
|
}
|
|
|
|
|
2015-03-13 03:32:39 +01:00
|
|
|
void Looper::rebuildEpollLocked() {
|
|
|
|
// Close old epoll instance if we have one.
|
|
|
|
if (mEpollFd >= 0) {
|
|
|
|
#if DEBUG_CALLBACKS
|
|
|
|
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
|
|
|
|
#endif
|
2018-07-19 02:26:24 +02:00
|
|
|
mEpollFd.reset();
|
2015-03-13 03:32:39 +01:00
|
|
|
}
|
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
// Allocate the new epoll instance and register the WakeEventFd.
|
2018-12-17 18:35:12 +01:00
|
|
|
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
|
2015-07-01 00:17:14 +02:00
|
|
|
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
|
2015-03-13 03:32:39 +01:00
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
|
|
|
|
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
|
2015-07-01 00:17:14 +02:00
|
|
|
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
|
|
|
|
strerror(errno));
|
2015-03-13 03:32:39 +01:00
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
for (const auto& [seq, request] : mRequests) {
|
|
|
|
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
|
2015-03-13 03:32:39 +01:00
|
|
|
|
2018-07-19 02:26:24 +02:00
|
|
|
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
|
2015-03-13 03:32:39 +01:00
|
|
|
if (epollResult < 0) {
|
2015-07-01 00:17:14 +02:00
|
|
|
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
|
|
|
|
request.fd, strerror(errno));
|
2015-03-13 03:32:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Looper::scheduleEpollRebuildLocked() {
|
|
|
|
if (!mEpollRebuildRequired) {
|
|
|
|
#if DEBUG_CALLBACKS
|
|
|
|
ALOGD("%p ~ scheduleEpollRebuildLocked - scheduling epoll set rebuild", this);
|
|
|
|
#endif
|
|
|
|
mEpollRebuildRequired = true;
|
|
|
|
wake();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-14 08:17:30 +02:00
|
|
|
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++);
|
2012-06-01 01:15:35 +02:00
|
|
|
int ident = response.request.ident;
|
|
|
|
if (ident >= 0) {
|
2011-03-02 23:41:58 +01:00
|
|
|
int fd = response.request.fd;
|
|
|
|
int events = response.events;
|
|
|
|
void* data = response.request.data;
|
2010-09-14 08:17:30 +02:00
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
|
2011-03-02 23:41:58 +01:00
|
|
|
"fd=%d, events=0x%x, data=%p",
|
|
|
|
this, ident, fd, events, data);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
2018-07-17 03:11:34 +02:00
|
|
|
if (outFd != nullptr) *outFd = fd;
|
|
|
|
if (outEvents != nullptr) *outEvents = events;
|
|
|
|
if (outData != nullptr) *outData = data;
|
2011-03-02 23:41:58 +01:00
|
|
|
return ident;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result != 0) {
|
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ pollOnce - returning result %d", this, result);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
2018-07-17 03:11:34 +02:00
|
|
|
if (outFd != nullptr) *outFd = 0;
|
|
|
|
if (outEvents != nullptr) *outEvents = 0;
|
|
|
|
if (outData != nullptr) *outData = nullptr;
|
2010-09-14 08:17:30 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = pollInner(timeoutMillis);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Looper::pollInner(int timeoutMillis) {
|
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
2010-10-06 00:35:37 +02:00
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// Adjust the timeout based on when the next message is due.
|
|
|
|
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
|
|
|
|
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
2011-03-17 09:34:19 +01:00
|
|
|
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
|
|
|
|
if (messageTimeoutMillis >= 0
|
|
|
|
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
|
|
|
|
timeoutMillis = messageTimeoutMillis;
|
2011-03-02 23:41:58 +01:00
|
|
|
}
|
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2015-03-11 02:31:12 +01:00
|
|
|
ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
|
2011-03-02 23:41:58 +01:00
|
|
|
this, mNextMessageUptime - now, timeoutMillis);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Poll.
|
2013-12-12 07:46:45 +01:00
|
|
|
int result = POLL_WAKE;
|
2010-10-06 00:35:37 +02:00
|
|
|
mResponses.clear();
|
|
|
|
mResponseIndex = 0;
|
|
|
|
|
2013-05-06 23:25:20 +02:00
|
|
|
// We are about to idle.
|
2015-02-26 23:16:30 +01:00
|
|
|
mPolling = true;
|
2013-05-06 23:25:20 +02:00
|
|
|
|
2010-09-14 08:17:30 +02:00
|
|
|
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
|
2018-07-19 02:26:24 +02:00
|
|
|
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
|
2010-10-06 00:35:37 +02:00
|
|
|
|
2013-05-06 23:25:20 +02:00
|
|
|
// No longer idling.
|
2015-02-26 23:16:30 +01:00
|
|
|
mPolling = false;
|
2013-05-06 23:25:20 +02:00
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// Acquire lock.
|
|
|
|
mLock.lock();
|
|
|
|
|
2015-03-13 03:32:39 +01:00
|
|
|
// Rebuild epoll set if needed.
|
|
|
|
if (mEpollRebuildRequired) {
|
|
|
|
mEpollRebuildRequired = false;
|
|
|
|
rebuildEpollLocked();
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// Check for poll error.
|
2010-09-14 08:17:30 +02:00
|
|
|
if (eventCount < 0) {
|
2010-09-17 02:04:52 +02:00
|
|
|
if (errno == EINTR) {
|
2010-10-06 00:35:37 +02:00
|
|
|
goto Done;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
2015-06-30 17:22:24 +02:00
|
|
|
ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
|
2013-12-12 07:46:45 +01:00
|
|
|
result = POLL_ERROR;
|
2010-10-06 00:35:37 +02:00
|
|
|
goto Done;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// Check for poll timeout.
|
2010-09-14 08:17:30 +02:00
|
|
|
if (eventCount == 0) {
|
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ pollOnce - timeout", this);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
2013-12-12 07:46:45 +01:00
|
|
|
result = POLL_TIMEOUT;
|
2010-10-06 00:35:37 +02:00
|
|
|
goto Done;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// Handle all events.
|
2010-09-14 08:17:30 +02:00
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
2010-10-06 00:35:37 +02:00
|
|
|
|
2010-09-18 02:01:23 +02:00
|
|
|
for (int i = 0; i < eventCount; i++) {
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
const SequenceNumber seq = eventItems[i].data.u64;
|
2010-09-18 02:01:23 +02:00
|
|
|
uint32_t epollEvents = eventItems[i].events;
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
if (seq == WAKE_EVENT_FD_SEQ) {
|
2010-09-18 02:01:23 +02:00
|
|
|
if (epollEvents & EPOLLIN) {
|
2010-10-06 00:35:37 +02:00
|
|
|
awoken();
|
2010-09-14 08:17:30 +02:00
|
|
|
} else {
|
2015-03-26 22:36:32 +01:00
|
|
|
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
|
2010-09-18 02:01:23 +02:00
|
|
|
}
|
|
|
|
} else {
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
const auto& request_it = mRequests.find(seq);
|
|
|
|
if (request_it != mRequests.end()) {
|
|
|
|
const auto& request = request_it->second;
|
2010-09-18 02:01:23 +02:00
|
|
|
int events = 0;
|
2013-12-12 07:46:45 +01:00
|
|
|
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
|
|
|
|
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
|
|
|
|
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
|
|
|
|
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
mResponses.push({.seq = seq, .events = events, .request = request});
|
2010-09-18 02:01:23 +02:00
|
|
|
} else {
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
ALOGW("Ignoring unexpected epoll events 0x%x for sequence number %" PRIu64
|
|
|
|
" that is no longer registered.",
|
|
|
|
epollEvents, seq);
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-10-06 00:35:37 +02:00
|
|
|
Done: ;
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
// Invoke pending message callbacks.
|
|
|
|
mNextMessageUptime = LLONG_MAX;
|
|
|
|
while (mMessageEnvelopes.size() != 0) {
|
|
|
|
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
|
|
|
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
|
|
|
|
if (messageEnvelope.uptime <= now) {
|
|
|
|
// Remove the envelope from the list.
|
|
|
|
// We keep a strong reference to the handler until the call to handleMessage
|
|
|
|
// finishes. Then we drop it so that the handler can be deleted *before*
|
|
|
|
// we reacquire our lock.
|
|
|
|
{ // obtain handler
|
|
|
|
sp<MessageHandler> handler = messageEnvelope.handler;
|
|
|
|
Message message = messageEnvelope.message;
|
|
|
|
mMessageEnvelopes.removeAt(0);
|
|
|
|
mSendingMessage = true;
|
|
|
|
mLock.unlock();
|
|
|
|
|
|
|
|
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
|
2011-03-02 23:41:58 +01:00
|
|
|
this, handler.get(), message.what);
|
|
|
|
#endif
|
|
|
|
handler->handleMessage(message);
|
|
|
|
} // release handler
|
|
|
|
|
|
|
|
mLock.lock();
|
|
|
|
mSendingMessage = false;
|
2013-12-12 07:46:45 +01:00
|
|
|
result = POLL_CALLBACK;
|
2011-03-02 23:41:58 +01:00
|
|
|
} else {
|
|
|
|
// The last message left at the head of the queue determines the next wakeup time.
|
|
|
|
mNextMessageUptime = messageEnvelope.uptime;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Release lock.
|
|
|
|
mLock.unlock();
|
|
|
|
|
|
|
|
// Invoke all response callbacks.
|
2010-09-14 08:17:30 +02:00
|
|
|
for (size_t i = 0; i < mResponses.size(); i++) {
|
2012-06-01 01:15:35 +02:00
|
|
|
Response& response = mResponses.editItemAt(i);
|
2013-12-12 07:46:45 +01:00
|
|
|
if (response.request.ident == POLL_CALLBACK) {
|
2011-03-02 23:41:58 +01:00
|
|
|
int fd = response.request.fd;
|
|
|
|
int events = response.events;
|
|
|
|
void* data = response.request.data;
|
2010-09-14 08:17:30 +02:00
|
|
|
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
|
2012-06-01 01:15:35 +02:00
|
|
|
this, response.request.callback.get(), fd, events, data);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
2015-03-11 02:31:12 +01:00
|
|
|
// Invoke the callback. Note that the file descriptor may be closed by
|
|
|
|
// the callback (and potentially even reused) before the function returns so
|
|
|
|
// we need to be a little careful when removing the file descriptor afterwards.
|
2012-06-01 01:15:35 +02:00
|
|
|
int callbackResult = response.request.callback->handleEvent(fd, events, data);
|
2010-09-14 08:17:30 +02:00
|
|
|
if (callbackResult == 0) {
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
AutoMutex _l(mLock);
|
|
|
|
removeSequenceNumberLocked(response.seq);
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
2015-03-11 02:31:12 +01:00
|
|
|
|
2012-06-01 01:15:35 +02:00
|
|
|
// Clear the callback reference in the response structure promptly because we
|
|
|
|
// will not clear the response vector itself until the next poll.
|
|
|
|
response.request.callback.clear();
|
2013-12-12 07:46:45 +01:00
|
|
|
result = POLL_CALLBACK;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
2013-12-12 07:46:45 +01:00
|
|
|
} while (result == POLL_CALLBACK);
|
2010-09-14 08:17:30 +02:00
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC)
|
|
|
|
+ milliseconds_to_nanoseconds(timeoutMillis);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int result = pollOnce(timeoutMillis, outFd, outEvents, outData);
|
2013-12-12 07:46:45 +01:00
|
|
|
if (result != POLL_CALLBACK) {
|
2010-09-14 08:17:30 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-03-17 09:34:19 +01:00
|
|
|
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
|
|
|
timeoutMillis = toMillisecondTimeoutDelay(now, endTime);
|
|
|
|
if (timeoutMillis == 0) {
|
2013-12-12 07:46:45 +01:00
|
|
|
return POLL_TIMEOUT;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Looper::wake() {
|
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ wake", this);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
|
|
|
|
2015-03-26 22:36:32 +01:00
|
|
|
uint64_t inc = 1;
|
2018-07-19 02:26:24 +02:00
|
|
|
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
|
2015-03-26 22:36:32 +01:00
|
|
|
if (nWrite != sizeof(uint64_t)) {
|
2010-09-14 08:17:30 +02:00
|
|
|
if (errno != EAGAIN) {
|
2019-04-01 21:22:55 +02:00
|
|
|
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
|
|
|
|
mWakeEventFd.get(), nWrite, strerror(errno));
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-06 00:35:37 +02:00
|
|
|
void Looper::awoken() {
|
|
|
|
#if DEBUG_POLL_AND_WAKE
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ awoken", this);
|
2010-10-06 00:35:37 +02:00
|
|
|
#endif
|
|
|
|
|
2015-03-26 22:36:32 +01:00
|
|
|
uint64_t counter;
|
2018-07-19 02:26:24 +02:00
|
|
|
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
|
2010-10-06 00:35:37 +02:00
|
|
|
}
|
|
|
|
|
2013-12-12 07:46:45 +01:00
|
|
|
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
|
2021-04-27 02:09:23 +02:00
|
|
|
sp<SimpleLooperCallback> looperCallback;
|
|
|
|
if (callback) {
|
|
|
|
looperCallback = sp<SimpleLooperCallback>::make(callback);
|
|
|
|
}
|
|
|
|
return addFd(fd, ident, events, looperCallback, data);
|
2012-06-01 01:15:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
|
2010-09-14 08:17:30 +02:00
|
|
|
#if DEBUG_CALLBACKS
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
|
2012-06-01 01:15:35 +02:00
|
|
|
events, callback.get(), data);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
|
|
|
|
2012-06-01 01:15:35 +02:00
|
|
|
if (!callback.get()) {
|
2010-09-14 08:17:30 +02:00
|
|
|
if (! mAllowNonCallbacks) {
|
2012-01-06 20:20:56 +01:00
|
|
|
ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
|
2010-09-14 08:17:30 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ident < 0) {
|
2012-06-01 01:15:35 +02:00
|
|
|
ALOGE("Invalid attempt to set NULL callback with ident < 0.");
|
2010-09-14 08:17:30 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-06-01 01:15:35 +02:00
|
|
|
} else {
|
2013-12-12 07:46:45 +01:00
|
|
|
ident = POLL_CALLBACK;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
{ // acquire lock
|
|
|
|
AutoMutex _l(mLock);
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
// There is a sequence number reserved for the WakeEventFd.
|
|
|
|
if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
|
|
|
|
const SequenceNumber seq = mNextRequestSeq++;
|
2010-09-14 08:17:30 +02:00
|
|
|
|
|
|
|
Request request;
|
|
|
|
request.fd = fd;
|
|
|
|
request.ident = ident;
|
2015-03-13 03:32:39 +01:00
|
|
|
request.events = events;
|
2010-09-14 08:17:30 +02:00
|
|
|
request.callback = callback;
|
|
|
|
request.data = data;
|
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
|
|
|
|
auto seq_it = mSequenceNumberByFd.find(fd);
|
|
|
|
if (seq_it == mSequenceNumberByFd.end()) {
|
2018-07-19 02:26:24 +02:00
|
|
|
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
|
2010-09-14 08:17:30 +02:00
|
|
|
if (epollResult < 0) {
|
2015-06-30 17:22:24 +02:00
|
|
|
ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
|
2010-09-14 08:17:30 +02:00
|
|
|
return -1;
|
|
|
|
}
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
mRequests.emplace(seq, request);
|
|
|
|
mSequenceNumberByFd.emplace(fd, seq);
|
2010-09-14 08:17:30 +02:00
|
|
|
} else {
|
2018-07-19 02:26:24 +02:00
|
|
|
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
|
2010-09-14 08:17:30 +02:00
|
|
|
if (epollResult < 0) {
|
2015-03-11 02:31:12 +01:00
|
|
|
if (errno == ENOENT) {
|
2015-03-13 03:32:39 +01:00
|
|
|
// Tolerate ENOENT because it means that an older file descriptor was
|
2015-03-11 02:31:12 +01:00
|
|
|
// closed before its callback was unregistered and meanwhile a new
|
|
|
|
// file descriptor with the same number has been created and is now
|
2015-03-13 03:32:39 +01:00
|
|
|
// being registered for the first time. This error may occur naturally
|
|
|
|
// when a callback has the side-effect of closing the file descriptor
|
|
|
|
// before returning and unregistering itself. Callback sequence number
|
|
|
|
// checks further ensure that the race is benign.
|
|
|
|
//
|
|
|
|
// Unfortunately due to kernel limitations we need to rebuild the epoll
|
|
|
|
// set from scratch because it may contain an old file handle that we are
|
|
|
|
// now unable to remove since its file descriptor is no longer valid.
|
|
|
|
// No such problem would have occurred if we were using the poll system
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
// call instead, but that approach carries other disadvantages.
|
2015-03-11 02:31:12 +01:00
|
|
|
#if DEBUG_CALLBACKS
|
|
|
|
ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
|
2015-07-01 00:17:14 +02:00
|
|
|
"being recycled, falling back on EPOLL_CTL_ADD: %s",
|
|
|
|
this, strerror(errno));
|
2015-03-11 02:31:12 +01:00
|
|
|
#endif
|
2018-07-19 02:26:24 +02:00
|
|
|
epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
|
2015-03-11 02:31:12 +01:00
|
|
|
if (epollResult < 0) {
|
2015-07-01 00:17:14 +02:00
|
|
|
ALOGE("Error modifying or adding epoll events for fd %d: %s",
|
|
|
|
fd, strerror(errno));
|
2015-03-11 02:31:12 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2015-03-13 03:32:39 +01:00
|
|
|
scheduleEpollRebuildLocked();
|
2015-03-11 02:31:12 +01:00
|
|
|
} else {
|
2015-07-01 00:17:14 +02:00
|
|
|
ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
|
2015-03-11 02:31:12 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
const SequenceNumber oldSeq = seq_it->second;
|
|
|
|
mRequests.erase(oldSeq);
|
|
|
|
mRequests.emplace(seq, request);
|
|
|
|
seq_it->second = seq;
|
2010-09-14 08:17:30 +02:00
|
|
|
}
|
|
|
|
} // release lock
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Looper::removeFd(int fd) {
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
AutoMutex _l(mLock);
|
|
|
|
const auto& it = mSequenceNumberByFd.find(fd);
|
|
|
|
if (it == mSequenceNumberByFd.end()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return removeSequenceNumberLocked(it->second);
|
2015-03-11 02:31:12 +01:00
|
|
|
}
|
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
|
2010-09-14 08:17:30 +02:00
|
|
|
#if DEBUG_CALLBACKS
|
2023-11-24 15:20:20 +01:00
|
|
|
ALOGD("%p ~ removeFd - seq=%" PRIu64, this, seq);
|
2010-09-14 08:17:30 +02:00
|
|
|
#endif
|
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
const auto& request_it = mRequests.find(seq);
|
|
|
|
if (request_it == mRequests.end()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
const int fd = request_it->second.fd;
|
|
|
|
|
|
|
|
// Always remove the FD from the request map even if an error occurs while
|
|
|
|
// updating the epoll set so that we avoid accidentally leaking callbacks.
|
|
|
|
mRequests.erase(request_it);
|
|
|
|
mSequenceNumberByFd.erase(fd);
|
|
|
|
|
|
|
|
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
|
|
|
|
if (epollResult < 0) {
|
|
|
|
if (errno == EBADF || errno == ENOENT) {
|
|
|
|
// Tolerate EBADF or ENOENT because it means that the file descriptor was closed
|
|
|
|
// before its callback was unregistered. This error may occur naturally when a
|
|
|
|
// callback has the side-effect of closing the file descriptor before returning and
|
|
|
|
// unregistering itself.
|
|
|
|
//
|
|
|
|
// Unfortunately due to kernel limitations we need to rebuild the epoll
|
|
|
|
// set from scratch because it may contain an old file handle that we are
|
|
|
|
// now unable to remove since its file descriptor is no longer valid.
|
|
|
|
// No such problem would have occurred if we were using the poll system
|
|
|
|
// call instead, but that approach carries other disadvantages.
|
2015-03-11 02:31:12 +01:00
|
|
|
#if DEBUG_CALLBACKS
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
|
|
|
|
"being closed: %s",
|
|
|
|
this, strerror(errno));
|
2015-03-11 02:31:12 +01:00
|
|
|
#endif
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
scheduleEpollRebuildLocked();
|
|
|
|
} else {
|
|
|
|
// Some other error occurred. This is really weird because it means
|
|
|
|
// our list of callbacks got out of sync with the epoll set somehow.
|
|
|
|
// We defensively rebuild the epoll set to avoid getting spurious
|
|
|
|
// notifications with nowhere to go.
|
|
|
|
ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
|
|
|
|
scheduleEpollRebuildLocked();
|
|
|
|
return -1;
|
2015-03-11 02:31:12 +01:00
|
|
|
}
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
}
|
2010-09-14 08:17:30 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-03-02 23:41:58 +01:00
|
|
|
void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
|
2011-04-13 07:39:53 +02:00
|
|
|
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
|
|
|
sendMessageAtTime(now, handler, message);
|
2011-03-02 23:41:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
|
|
|
|
const Message& message) {
|
|
|
|
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
|
|
|
sendMessageAtTime(now + uptimeDelay, handler, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
|
|
|
|
const Message& message) {
|
|
|
|
#if DEBUG_CALLBACKS
|
2015-03-11 02:31:12 +01:00
|
|
|
ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d",
|
2011-03-02 23:41:58 +01:00
|
|
|
this, uptime, handler.get(), message.what);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
{ // acquire lock
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
|
|
|
size_t messageCount = mMessageEnvelopes.size();
|
|
|
|
while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageEnvelope messageEnvelope(uptime, handler, message);
|
|
|
|
mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
|
|
|
|
|
|
|
|
// Optimization: If the Looper is currently sending a message, then we can skip
|
|
|
|
// the call to wake() because the next thing the Looper will do after processing
|
|
|
|
// messages is to decide when the next wakeup time should be. In fact, it does
|
|
|
|
// not even matter whether this code is running on the Looper thread.
|
|
|
|
if (mSendingMessage) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} // release lock
|
|
|
|
|
|
|
|
// Wake the poll loop only when we enqueue a new message at the head.
|
|
|
|
if (i == 0) {
|
|
|
|
wake();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Looper::removeMessages(const sp<MessageHandler>& handler) {
|
|
|
|
#if DEBUG_CALLBACKS
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ removeMessages - handler=%p", this, handler.get());
|
2011-03-02 23:41:58 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
{ // acquire lock
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
|
|
|
for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
|
|
|
|
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
|
|
|
|
if (messageEnvelope.handler == handler) {
|
|
|
|
mMessageEnvelopes.removeAt(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // release lock
|
|
|
|
}
|
|
|
|
|
|
|
|
void Looper::removeMessages(const sp<MessageHandler>& handler, int what) {
|
|
|
|
#if DEBUG_CALLBACKS
|
2011-12-20 17:23:08 +01:00
|
|
|
ALOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what);
|
2011-03-02 23:41:58 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
{ // acquire lock
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
|
|
|
for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
|
|
|
|
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
|
|
|
|
if (messageEnvelope.handler == handler
|
|
|
|
&& messageEnvelope.message.what == what) {
|
|
|
|
mMessageEnvelopes.removeAt(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // release lock
|
|
|
|
}
|
|
|
|
|
2015-02-26 23:16:30 +01:00
|
|
|
bool Looper::isPolling() const {
|
|
|
|
return mPolling;
|
2013-05-06 23:25:20 +02:00
|
|
|
}
|
|
|
|
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
uint32_t Looper::Request::getEpollEvents() const {
|
|
|
|
uint32_t epollEvents = 0;
|
2015-03-13 03:32:39 +01:00
|
|
|
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
|
|
|
|
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
|
Looper: Use sequence numbers in epoll_event to track requests
Previously, Looper internally kept track of the requests to add fds
using the fd value itself. It used an internal sequence number
associated with each request to add a callback to avoid a situation
where a callback is unexpectedly called. However, since it used the fd
value rather than the sequence number to register events into epoll,
there was still a way where unintended hangups could occur.
This exact sequence of events caused unintended behavior in Looper:
- An fd (FD) is added to the looper.
- Looper registers FD into epoll using its FD number.
- FD is closed.
- A hangup event arrives from epoll_wait while the Looper is polling.
Looper is waiting for the lock to process the callback for FD, because
it is blocked by:
- A new fd is created and added to the looper. Since the lowest number
fd is reused, this new fd has the same value as FD.
- The poll request for Looper is now unblocked, so it looks up the
callback associated with FD to process the hangup.
- Since FD is already associated with the new callback, the new callback
is called unintentionally.
This CL uses the sequence number to register fds into epoll. That way,
when we get a hangup from epoll that is associated with a sequence
number, there is no way an unexpected callback will called.
This CL also adds a test to verify this behavior. Due to the
nondeterministic nature of this multi-thread scenario, the test verifies
this scenario repeatedly. Without the fix in Looper, the test is flaky,
but should never fail after the fix.
Bug: 195020232
Bug: 189135695
Test: atest libutils_test --rerun-until-failure
Ignore-AOSP-First: Topic CL aosp/1799831 has a merge conflict with
internal master, resolved in ag/15613419.
Change-Id: Ib4edab7f2407adaef6a1708b29bc52634f25dbb6
2021-08-12 21:36:31 +02:00
|
|
|
return epollEvents;
|
2015-03-13 03:32:39 +01:00
|
|
|
}
|
|
|
|
|
2016-09-16 03:15:37 +02:00
|
|
|
MessageHandler::~MessageHandler() { }
|
|
|
|
|
|
|
|
LooperCallback::~LooperCallback() { }
|
|
|
|
|
2010-09-14 08:17:30 +02:00
|
|
|
} // namespace android
|