]>
git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/selector.cpp
2 * Copyright (c) 2000-2001,2003-2004,2011,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // selector - I/O stream multiplexing
29 #include <security_utilities/errors.h>
30 #include <security_utilities/debugging.h>
31 #include <algorithm> // min/max
35 namespace UnixPlusPlus
{
39 // construct a Selector object.
41 Selector::Selector() : fdMin(INT_MAX
), fdMax(-1)
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
);
55 // Add a Client to a Selector
57 void Selector::add(int fd
, Client
&client
, Type type
)
59 // plausibility checks
60 assert(!client
.isActive()); // one Selector per client, and no re-adding
63 secdebug("selector", "add client %p fd %d type=%d", &client
, fd
, type
);
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
);
81 Client
* &slot
= clientMap
[fd
];
85 client
.mSelector
= this;
86 client
.mEvents
= type
;
92 // Remove a Client from a Selector
94 void Selector::remove(int fd
)
98 ClientMap::iterator it
= clientMap
.find(fd
);
99 assert(it
!= clientMap
.end());
100 assert(it
->second
->mSelector
== this);
102 secdebug("selector", "remove client %p fd %d", it
->second
, fd
);
104 // remove from FDSets
108 it
->second
->mSelector
= NULL
;
111 // recompute fdMin/fdMax if needed
115 } else if (fd
== fdMin
) {
116 fdMin
= clientMap
.begin()->first
;
117 } else if (fd
== fdMax
) {
118 fdMax
= clientMap
.rbegin()->first
;
124 // Adjust the FDSets for a single given Client according to a new event Type mask.
126 void Selector::set(int fd
, Type type
)
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
);
136 void Selector::operator () ()
138 if (!clientMap
.empty())
143 void Selector::operator () (Time::Absolute stopTime
)
145 if (!clientMap
.empty())
146 singleStep(stopTime
- Time::now());
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.
156 void Selector::singleStep(Time::Interval maxWait
)
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;
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
),
175 secdebug("selector", "select failed: errno=%d", errno
);
176 UnixError::throwMe();
178 secdebug("selector", "select returned nothing");
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
++) {
187 if (inSet
[fd
]) types
|= input
;
188 if (outSet
[fd
]) types
|= output
;
189 if (errSet
[fd
]) types
|= critical
;
191 secdebug("selector", "notify fd %d client %p type %d",
192 fd
, clientMap
[fd
], types
);
193 clientMap
[fd
]->notify(fd
, types
);
203 } // end namespace IPPlusPlus
204 } // end namespace Security