blob: 8dcbcabf841b039ff47d30371be886f5c2b2b203 [file] [log] [blame]
/*
* Copyright 2018, 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.
*/
#include "poller.h"
#include "log.h"
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unordered_map>
#include <vector>
using std::chrono::duration_cast;
static struct timespec* calculateTimeout(Pollable::Timestamp deadline,
struct timespec* ts) {
Pollable::Timestamp now = Pollable::Clock::now();
if (deadline < Pollable::Timestamp::max()) {
if (deadline <= now) {
LOGE("Poller found past due deadline, setting to zero");
ts->tv_sec = 0;
ts->tv_nsec = 0;
return ts;
}
auto timeout = deadline - now;
// Convert and round down to seconds
auto seconds = duration_cast<std::chrono::seconds>(timeout);
// Then subtract the seconds from the timeout and convert the remainder
auto nanos = duration_cast<std::chrono::nanoseconds>(timeout - seconds);
ts->tv_sec = seconds.count();
ts->tv_nsec = nanos.count();
return ts;
}
return nullptr;
}
Poller::Poller() {
}
void Poller::addPollable(Pollable* pollable) {
mPollables.push_back(pollable);
}
int Poller::run() {
// Block all signals while we're running. This way we don't have to deal
// with things like EINTR. We then uses ppoll to set the original mask while
// polling. This way polling can be interrupted but socket writing, reading
// and ioctl remain interrupt free. If a signal arrives while we're blocking
// it it will be placed in the signal queue and handled once ppoll sets the
// original mask. This way no signals are lost.
sigset_t blockMask, mask;
int status = ::sigfillset(&blockMask);
if (status != 0) {
LOGE("Unable to fill signal set: %s", strerror(errno));
return errno;
}
status = ::sigprocmask(SIG_SETMASK, &blockMask, &mask);
if (status != 0) {
LOGE("Unable to set signal mask: %s", strerror(errno));
return errno;
}
std::vector<struct pollfd> fds;
std::unordered_map<int, Pollable*> pollables;
while (true) {
fds.clear();
pollables.clear();
Pollable::Timestamp deadline = Pollable::Timestamp::max();
for (auto& pollable : mPollables) {
size_t start = fds.size();
pollable->getPollData(&fds);
Pollable::Timestamp pollableDeadline = pollable->getTimeout();
// Create a map from each fd to the pollable
for (size_t i = start; i < fds.size(); ++i) {
pollables[fds[i].fd] = pollable;
}
if (pollableDeadline < deadline) {
deadline = pollableDeadline;
}
}
struct timespec ts = { 0, 0 };
struct timespec* tsPtr = calculateTimeout(deadline, &ts);
status = ::ppoll(fds.data(), fds.size(), tsPtr, &mask);
if (status < 0) {
if (errno == EINTR) {
// Interrupted, just keep going
continue;
}
// Actual error, time to quit
LOGE("Polling failed: %s", strerror(errno));
return errno;
} else if (status > 0) {
// Check for read or close events
for (const auto& fd : fds) {
if ((fd.revents & (POLLIN | POLLHUP)) == 0) {
// Neither POLLIN nor POLLHUP, not interested
continue;
}
auto pollable = pollables.find(fd.fd);
if (pollable == pollables.end()) {
// No matching fd, weird and unexpected
LOGE("Poller could not find fd matching %d", fd.fd);
continue;
}
if (fd.revents & POLLIN) {
// This pollable has data available for reading
int status = 0;
if (!pollable->second->onReadAvailable(fd.fd, &status)) {
// The onReadAvailable handler signaled an exit
return status;
}
}
if (fd.revents & POLLHUP) {
// The fd was closed from the other end
int status = 0;
if (!pollable->second->onClose(fd.fd, &status)) {
// The onClose handler signaled an exit
return status;
}
}
}
}
// Check for timeouts
Pollable::Timestamp now = Pollable::Clock::now();
for (const auto& pollable : mPollables) {
if (pollable->getTimeout() <= now) {
int status = 0;
if (!pollable->onTimeout(&status)) {
// The onTimeout handler signaled an exit
return status;
}
}
}
}
return 0;
}