]> git.saurik.com Git - apple/security.git/blame - libsecurity_utilities/lib/selector.cpp
Security-55471.14.18.tar.gz
[apple/security.git] / libsecurity_utilities / lib / selector.cpp
CommitLineData
b1ab9ed8
A
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