X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_utilities/lib/selector.cpp?ds=inline diff --git a/libsecurity_utilities/lib/selector.cpp b/libsecurity_utilities/lib/selector.cpp new file mode 100644 index 00000000..d636ac7e --- /dev/null +++ b/libsecurity_utilities/lib/selector.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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