/* * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ // // selector - I/O stream multiplexing // #include "selector.h" #include #include #include // min/max namespace Security { namespace UnixPlusPlus { // // construct a Selector object. // Selector::Selector() : fdMin(INT_MAX), fdMax(-1) { // initially allocate room for FD_SETSIZE file descriptors (usually good enough) fdSetSize = FD_SETSIZE / NFDBITS; inSet.grow(0, fdSetSize); outSet.grow(0, fdSetSize); errSet.grow(0, fdSetSize); } Selector::~Selector() { } // // Add a Client to a Selector // void Selector::add(int fd, Client &client, Type type) { // plausibility checks assert(!client.isActive()); // one Selector per client, and no re-adding assert(fd >= 0); secdebug("selector", "add client %p fd %d type=%d", &client, fd, type); // grow FDSets if needed unsigned int pos = fd / NFDBITS; if (pos >= fdSetSize) { int newSize = (fd - 1) / NFDBITS + 2; // as much as needed + 1 spare word inSet.grow(fdSetSize, newSize); outSet.grow(fdSetSize, newSize); errSet.grow(fdSetSize, newSize); } // adjust boundaries if (fd < fdMin) fdMin = fd; if (fd > fdMax) fdMax = fd; // add client Client * &slot = clientMap[fd]; assert(!slot); slot = &client; client.mFd = fd; client.mSelector = this; client.mEvents = type; set(fd, type); } // // Remove a Client from a Selector // void Selector::remove(int fd) { // sanity checks assert(fd >= 0); ClientMap::iterator it = clientMap.find(fd); assert(it != clientMap.end()); assert(it->second->mSelector == this); secdebug("selector", "remove client %p fd %d", it->second, fd); // remove from FDSets set(fd, none); // remove client it->second->mSelector = NULL; clientMap.erase(it); // recompute fdMin/fdMax if needed if (isEmpty()) { fdMin = INT_MAX; fdMax = -1; } else if (fd == fdMin) { fdMin = clientMap.begin()->first; } else if (fd == fdMax) { fdMax = clientMap.rbegin()->first; } } // // Adjust the FDSets for a single given Client according to a new event Type mask. // void Selector::set(int fd, Type type) { assert(fd >= 0); inSet.set(fd, type & input); outSet.set(fd, type & output); errSet.set(fd, type & critical); secdebug("selector", "fd %d notifications 0x%x", fd, type); } void Selector::operator () () { if (!clientMap.empty()) singleStep(0); } void Selector::operator () (Time::Absolute stopTime) { if (!clientMap.empty()) singleStep(stopTime - Time::now()); } // // Perform a single pass through the Selector and notify all clients // that have selected I/O pending at this time. // There is not time limit on how long this may take; if the clients // are well written, it won't be too long. // void Selector::singleStep(Time::Interval maxWait) { assert(!clientMap.empty()); secdebug("selector", "select(%d) [%d-%d] for %ld clients", fdMax + 1, fdMin, fdMax, clientMap.size()); for (;;) { // pseudo-loop - only retries struct timeval duration = maxWait.timevalInterval(); #if defined(__APPLE__) // ad-hoc fix: MacOS X's BSD rejects times of more than 100E6 seconds if (duration.tv_sec > 100000000) duration.tv_sec = 100000000; #endif const int size = FDSet::words(fdMax); // number of active words in sets switch (int hits = ::select(fdMax + 1, inSet.make(size), outSet.make(size), errSet.make(size), &duration)) { case -1: // error if (errno == EINTR) continue; secdebug("selector", "select failed: errno=%d", errno); UnixError::throwMe(); case 0: // no events secdebug("selector", "select returned nothing"); return; default: // some events secdebug("selector", "%d pending descriptors", hits); //@@@ This could be optimized as a word-merge scan. //@@@ The typical case doesn't benefit from this though, though browsers might //@@@ and integrated servers definitely would. for (int fd = fdMin; fd <= fdMax && hits > 0; fd++) { int types = 0; if (inSet[fd]) types |= input; if (outSet[fd]) types |= output; if (errSet[fd]) types |= critical; if (types) { secdebug("selector", "notify fd %d client %p type %d", fd, clientMap[fd], types); clientMap[fd]->notify(fd, types); hits--; } } return; } } } } // end namespace IPPlusPlus } // end namespace Security