/* * 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 #include #include #include #include #include #include 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) { ts->tv_sec = 0; ts->tv_nsec = 0; return ts; } auto timeout = deadline - now; // Convert and round down to seconds auto seconds = duration_cast(timeout); // Then subtract the seconds from the timeout and convert the remainder auto nanos = duration_cast(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) { ALOGE("Unable to fill signal set: %s", strerror(errno)); return errno; } status = ::sigprocmask(SIG_SETMASK, &blockMask, &mask); if (status != 0) { ALOGE("Unable to set signal mask: %s", strerror(errno)); return errno; } std::vector fds; std::unordered_map 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 ALOGE("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 ALOGE("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(now, &status)) { // The onTimeout handler signaled an exit return status; } } } } return 0; }