/* * 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