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