]>
Commit | Line | Data |
---|---|---|
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 | ||
34 | namespace Security { | |
35 | namespace UnixPlusPlus { | |
36 | ||
37 | ||
38 | // | |
39 | // construct a Selector object. | |
40 | // | |
41 | Selector::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 | ||
50 | Selector::~Selector() | |
51 | { } | |
52 | ||
53 | ||
54 | // | |
55 | // Add a Client to a Selector | |
56 | // | |
57 | void 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 | // | |
94 | void 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 | // | |
126 | void 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 | ||
136 | void Selector::operator () () | |
137 | { | |
138 | if (!clientMap.empty()) | |
139 | singleStep(0); | |
140 | } | |
141 | ||
142 | ||
143 | void 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 | // | |
156 | void 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 |