]>
git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/selector.cpp
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // selector - I/O stream multiplexing
23 #include <Security/debugging.h>
24 #include <algorithm> // min/max
28 namespace UnixPlusPlus
{
32 // construct a Selector object.
34 Selector::Selector() : fdMin(INT_MAX
), fdMax(-1)
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
);
48 // Add a Client to a Selector
50 void Selector::add(int fd
, Client
&client
, Type type
)
52 // plausibility checks
53 assert(!client
.isActive()); // one Selector per client, and no re-adding
56 secdebug("selector", "add client %p fd %d type=%d", &client
, fd
, type
);
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
);
74 Client
* &slot
= clientMap
[fd
];
78 client
.mSelector
= this;
79 client
.mEvents
= type
;
85 // Remove a Client from a Selector
87 void Selector::remove(int fd
)
91 ClientMap::iterator it
= clientMap
.find(fd
);
92 assert(it
!= clientMap
.end());
93 assert(it
->second
->mSelector
== this);
95 secdebug("selector", "remove client %p fd %d", it
->second
, fd
);
101 it
->second
->mSelector
= NULL
;
104 // recompute fdMin/fdMax if needed
108 } else if (fd
== fdMin
) {
109 fdMin
= clientMap
.begin()->first
;
110 } else if (fd
== fdMax
) {
111 fdMax
= clientMap
.rbegin()->first
;
117 // Adjust the FDSets for a single given Client according to a new event Type mask.
119 void Selector::set(int fd
, Type type
)
122 inSet
.set(fd
, type
& input
);
123 outSet
.set(fd
, type
& output
);
124 errSet
.set(fd
, type
& critical
);
125 secdebug("selector", "fd %d notifications 0x%x", fd
, type
);
129 void Selector::operator () ()
131 if (!clientMap
.empty())
136 void Selector::operator () (Time::Absolute stopTime
)
138 if (!clientMap
.empty())
139 singleStep(stopTime
- Time::now());
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.
149 void Selector::singleStep(Time::Interval maxWait
)
151 assert(!clientMap
.empty());
152 secdebug("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;
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
),
168 secdebug("selector", "select failed: errno=%d", errno
);
169 UnixError::throwMe();
171 secdebug("selector", "select returned nothing");
173 default: // some events
174 secdebug("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
++) {
180 if (inSet
[fd
]) types
|= input
;
181 if (outSet
[fd
]) types
|= output
;
182 if (errSet
[fd
]) types
|= critical
;
184 secdebug("selector", "notify fd %d client %p type %d",
185 fd
, clientMap
[fd
], types
);
186 clientMap
[fd
]->notify(fd
, types
);
196 } // end namespace IPPlusPlus
197 } // end namespace Security