]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/selector.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / selector.cpp
1 /*
2 * Copyright (c) 2000-2001,2003-2004,2011,2014 Apple 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