/*
* Copyright 2011 Steven Gribble
*
* This file is part of the UW CSE 333 course project sequence
* (333proj).
*
* 333proj is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 333proj is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 333proj. If not, see .
*/
#include
#include
#include "./EventLoop.h"
namespace hw4 {
#define INITIAL_NUM_FDS 1024
EventLoop::EventLoop() {
fds_ = new struct pollfd[INITIAL_NUM_FDS];
fds_size_ = INITIAL_NUM_FDS;
registrations_updated_ = false;
quit_ = false;
// Set things up to ignore SIGPIPE. Without this, our process would
// receive a SIGPIPE signal when a client closes its end of a TCP
// socket and then a handler is tries to write to it.
struct sigaction newaction;
newaction.sa_handler = SIG_IGN;
sigemptyset(&newaction.sa_mask);
newaction.sa_flags = 0;
sigaction(SIGPIPE, &newaction, NULL);
}
EventLoop::~EventLoop() {
delete[] fds_;
fds_size_ = 0;
}
bool EventLoop::RegisterFD(const int fd,
const unsigned int events,
EventLoopHandler *eh) {
// Set up the Registration structure.
Registration r = {fd, events, eh};
// Insert it into the map.
fd_registrations_[fd] = r;
// Update the "registrations_updated_" flags.
registrations_updated_ = true;
return true;
}
bool EventLoop::UnregisterFD(const int fd) {
// Remove the registration from the map.
if (fd_registrations_.find(fd) == fd_registrations_.end())
return false;
Registration reg = fd_registrations_[fd];
if (fd_registrations_.erase(fd) == 0) {
assert(0);
}
// Store it in the list of deregistrations to be processed at
// the end of the event loop iteration.
fd_deregistrations_.push_back(reg.eh);
// Set the "something changed" flag.
registrations_updated_ = true;
return true;
}
void EventLoop::Quit() {
// By setting quit_ to true, the event loop will exit on the next
// iteration start. This means all handlers in the current iteration
// will complete, and then all handlers will be deregistered and
// deleted.
quit_ = true;
}
void EventLoop::Run() {
// Make sure the fds_ array is initialized prepped.
UpdateFDS();
// This is the main event loop.
while ((!quit_) && (fd_registrations_.size() > 0)) {
// Prepare to invoke the poll() system call. ("man 3 poll")
int num_fds = fd_registrations_.size();
// Do the poll. We rely on UpdateFDS() to keep the fds_ array
// in good shape.
int num_selected = poll(fds_, fd_registrations_.size(), -1);
if (num_selected > 0) {
// Polling succeeded. Loop through the fds_ array and service
// the selected fds by invoking their handler.
for (int i = 0; i < num_fds; i++) {
if (fds_[i].revents) {
// Service this fd. Lookup the handler registration in the
// fd_registrations_ map.
Registration x = fd_registrations_[fds_[i].fd];
unsigned int eventfield = 0;
// Set up the eventfield bitfield to pass to the handler.
if ((x.events & FD_IS_READABLE) && (fds_[i].revents & POLLIN))
eventfield |= FD_IS_READABLE;
if ((x.events & FD_IS_WRITEABLE) && (fds_[i].revents & POLLOUT))
eventfield |= FD_IS_WRITEABLE;
if ((x.events & FD_ERR) && (fds_[i].revents & POLLERR))
eventfield |= FD_ERR;
if ((x.events & FD_HUP) && (fds_[i].revents & POLLHUP))
eventfield |= FD_HUP;
// Invoke the registered EventLoopHandler, passing it the
// event bitfield.
x.eh->Handle(x.fd, eventfield, this);
}
// Clear the fds_[i].revents field, now that we have
// dispatched the event. This ensures that on the next poll,
// we'll only dispatch newly set event conditions.
fds_[i].revents = 0;
}
}
// During the servicing of fds, the invoked EventLoopHandlers may
// have called RegisterFD or UnregisterFD. If so, the
// registrations_updated_ flag is set to true, meaning we need
// to update the fds_ array and delete the unregistered handlers.
if (registrations_updated_) {
// Update the fds_array.
UpdateFDS();
// Process the deregistrations; delete the handlers.
while (fd_deregistrations_.size() > 0) {
EventLoopHandler *eh = fd_deregistrations_.front();
fd_deregistrations_.pop_front();
delete eh;
}
}
}
// We're about to quit out of the event loop. Loop through the
// remaining handlers, invoke Quitting() on them, then deleting
// them. Make sure we never double-delete a handler.
while (fd_registrations_.size() > 0) {
Registration x = (*(fd_registrations_.begin())).second;
fd_registrations_.erase(fd_registrations_.begin());
x.eh->Quitting(x.fd);
std::map::iterator it;
bool done = false;
// Loop over the registrations, looking for another registered
// version of this handler. If we find it, remove it from the
// registrations array to avoid double-deleting it.
while (!done) {
done = true;
for (it = fd_registrations_.begin();
it != fd_registrations_.end();
it++) {
if ((*it).second.eh == x.eh) {
// We found one, remove it from the registrations table and
// so set "done" to false to loop back and look for more.
done = false;
fd_registrations_.erase(it);
break;
}
}
}
// OK, it's safe to delete this guy now.
delete x.eh;
}
}
void EventLoop::UpdateFDS(void) {
// This is invoked by an event handler to indicate it that it is
// either (a) a new handler that wants to start receiving events,
// or (b) an existing handler that is changing the set of events
// that it is interested in.
// First, make sure that the fds_ array is big enough to store all
// of the handler fds_ that are registered.
if (fd_registrations_.size() > fds_size_) {
// We need to grow the array; we'll grow by INITIAL_NUM_FDS.
fds_size_ += INITIAL_NUM_FDS;
delete[] fds_;
fds_ = new struct pollfd[fds_size_];
}
// Loop through the registrations map, updating the fds_ array.
int count = 0;
std::map::iterator it;
for (it = fd_registrations_.begin();
it != fd_registrations_.end();
it++) {
// Set up the fds_[count] structure element associated with this
// registration.
unsigned int eventfield = ((*it).second).events;
fds_[count].fd = (*it).first;
fds_[count].events = fds_[count].revents = 0;
// Set the events flags that the customer is interested in.
if (eventfield & FD_IS_READABLE)
fds_[count].events |= POLLIN;
if (eventfield & FD_IS_WRITEABLE)
fds_[count].events |= POLLOUT;
if (eventfield & FD_ERR)
fds_[count].events |= POLLERR;
if (eventfield & FD_HUP)
fds_[count].events |= POLLHUP;
count++;
}
// Clear the registrations_updated_ flag.
registrations_updated_ = false;
}
} // namespace hw4