1/* 2 * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// selector - I/O stream multiplexing 27// 28#include "selector.h" 29#include <security_utilities/errors.h> 30#include <security_utilities/debugging.h> 31#include <algorithm> // min/max 32 33 34namespace Security { 35namespace UnixPlusPlus { 36 37 38// 39// construct a Selector object. 40// 41Selector::Selector() : fdMin(INT_MAX), fdMax(-1) 42{ 43 // initially allocate room for FD_SETSIZE file descriptors (usually good enough) 44 fdSetSize = FD_SETSIZE / NFDBITS; 45 inSet.grow(0, fdSetSize); 46 outSet.grow(0, fdSetSize); 47 errSet.grow(0, fdSetSize); 48} 49 50Selector::~Selector() 51{ } 52 53 54// 55// Add a Client to a Selector 56// 57void Selector::add(int fd, Client &client, Type type) 58{ 59 // plausibility checks 60 assert(!client.isActive()); // one Selector per client, and no re-adding 61 assert(fd >= 0); 62 63 secdebug("selector", "add client %p fd %d type=%d", &client, fd, type); 64 65 // grow FDSets if needed 66 unsigned int pos = fd / NFDBITS; 67 if (pos >= fdSetSize) { 68 int newSize = (fd - 1) / NFDBITS + 2; // as much as needed + 1 spare word 69 inSet.grow(fdSetSize, newSize); 70 outSet.grow(fdSetSize, newSize); 71 errSet.grow(fdSetSize, newSize); 72 } 73 74 // adjust boundaries 75 if (fd < fdMin) 76 fdMin = fd; 77 if (fd > fdMax) 78 fdMax = fd; 79 80 // add client 81 Client * &slot = clientMap[fd]; 82 assert(!slot); 83 slot = &client; 84 client.mFd = fd; 85 client.mSelector = this; 86 client.mEvents = type; 87 set(fd, type); 88} 89 90 91// 92// Remove a Client from a Selector 93// 94void Selector::remove(int fd) 95{ 96 // sanity checks 97 assert(fd >= 0); 98 ClientMap::iterator it = clientMap.find(fd); 99 assert(it != clientMap.end()); 100 assert(it->second->mSelector == this); 101 102 secdebug("selector", "remove client %p fd %d", it->second, fd); 103 104 // remove from FDSets 105 set(fd, none); 106 107 // remove client 108 it->second->mSelector = NULL; 109 clientMap.erase(it); 110 111 // recompute fdMin/fdMax if needed 112 if (isEmpty()) { 113 fdMin = INT_MAX; 114 fdMax = -1; 115 } else if (fd == fdMin) { 116 fdMin = clientMap.begin()->first; 117 } else if (fd == fdMax) { 118 fdMax = clientMap.rbegin()->first; 119 } 120} 121 122 123// 124// Adjust the FDSets for a single given Client according to a new event Type mask. 125// 126void Selector::set(int fd, Type type) 127{ 128 assert(fd >= 0); 129 inSet.set(fd, type & input); 130 outSet.set(fd, type & output); 131 errSet.set(fd, type & critical); 132 secdebug("selector", "fd %d notifications 0x%x", fd, type); 133} 134 135 136void Selector::operator () () 137{ 138 if (!clientMap.empty()) 139 singleStep(0); 140} 141 142 143void Selector::operator () (Time::Absolute stopTime) 144{ 145 if (!clientMap.empty()) 146 singleStep(stopTime - Time::now()); 147} 148 149 150// 151// Perform a single pass through the Selector and notify all clients 152// that have selected I/O pending at this time. 153// There is not time limit on how long this may take; if the clients 154// are well written, it won't be too long. 155// 156void Selector::singleStep(Time::Interval maxWait) 157{ 158 assert(!clientMap.empty()); 159 secdebug("selector", "select(%d) [%d-%d] for %ld clients", 160 fdMax + 1, fdMin, fdMax, clientMap.size()); 161 for (;;) { // pseudo-loop - only retries 162 struct timeval duration = maxWait.timevalInterval(); 163#if defined(__APPLE__) 164 // ad-hoc fix: MacOS X's BSD rejects times of more than 100E6 seconds 165 if (duration.tv_sec > 100000000) 166 duration.tv_sec = 100000000; 167#endif 168 const int size = FDSet::words(fdMax); // number of active words in sets 169 switch (int hits = ::select(fdMax + 1, 170 inSet.make(size), outSet.make(size), errSet.make(size), 171 &duration)) { 172 case -1: // error 173 if (errno == EINTR) 174 continue; 175 secdebug("selector", "select failed: errno=%d", errno); 176 UnixError::throwMe(); 177 case 0: // no events 178 secdebug("selector", "select returned nothing"); 179 return; 180 default: // some events 181 secdebug("selector", "%d pending descriptors", hits); 182 //@@@ This could be optimized as a word-merge scan. 183 //@@@ The typical case doesn't benefit from this though, though browsers might 184 //@@@ and integrated servers definitely would. 185 for (int fd = fdMin; fd <= fdMax && hits > 0; fd++) { 186 int types = 0; 187 if (inSet[fd]) types |= input; 188 if (outSet[fd]) types |= output; 189 if (errSet[fd]) types |= critical; 190 if (types) { 191 secdebug("selector", "notify fd %d client %p type %d", 192 fd, clientMap[fd], types); 193 clientMap[fd]->notify(fd, types); 194 hits--; 195 } 196 } 197 return; 198 } 199 } 200} 201 202 203} // end namespace IPPlusPlus 204} // end namespace Security 205