--- /dev/null
+/*
+ * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+//
+// selector - I/O stream multiplexing
+//
+#include "selector.h"
+#include <security_utilities/errors.h>
+#include <security_utilities/debugging.h>
+#include <algorithm> // min/max
+
+
+namespace Security {
+namespace UnixPlusPlus {
+
+
+//
+// construct a Selector object.
+//
+Selector::Selector() : fdMin(INT_MAX), fdMax(-1)
+{
+ // initially allocate room for FD_SETSIZE file descriptors (usually good enough)
+ fdSetSize = FD_SETSIZE / NFDBITS;
+ inSet.grow(0, fdSetSize);
+ outSet.grow(0, fdSetSize);
+ errSet.grow(0, fdSetSize);
+}
+
+Selector::~Selector()
+{ }
+
+
+//
+// Add a Client to a Selector
+//
+void Selector::add(int fd, Client &client, Type type)
+{
+ // plausibility checks
+ assert(!client.isActive()); // one Selector per client, and no re-adding
+ assert(fd >= 0);
+
+ secdebug("selector", "add client %p fd %d type=%d", &client, fd, type);
+
+ // grow FDSets if needed
+ unsigned int pos = fd / NFDBITS;
+ if (pos >= fdSetSize) {
+ int newSize = (fd - 1) / NFDBITS + 2; // as much as needed + 1 spare word
+ inSet.grow(fdSetSize, newSize);
+ outSet.grow(fdSetSize, newSize);
+ errSet.grow(fdSetSize, newSize);
+ }
+
+ // adjust boundaries
+ if (fd < fdMin)
+ fdMin = fd;
+ if (fd > fdMax)
+ fdMax = fd;
+
+ // add client
+ Client * &slot = clientMap[fd];
+ assert(!slot);
+ slot = &client;
+ client.mFd = fd;
+ client.mSelector = this;
+ client.mEvents = type;
+ set(fd, type);
+}
+
+
+//
+// Remove a Client from a Selector
+//
+void Selector::remove(int fd)
+{
+ // sanity checks
+ assert(fd >= 0);
+ ClientMap::iterator it = clientMap.find(fd);
+ assert(it != clientMap.end());
+ assert(it->second->mSelector == this);
+
+ secdebug("selector", "remove client %p fd %d", it->second, fd);
+
+ // remove from FDSets
+ set(fd, none);
+
+ // remove client
+ it->second->mSelector = NULL;
+ clientMap.erase(it);
+
+ // recompute fdMin/fdMax if needed
+ if (isEmpty()) {
+ fdMin = INT_MAX;
+ fdMax = -1;
+ } else if (fd == fdMin) {
+ fdMin = clientMap.begin()->first;
+ } else if (fd == fdMax) {
+ fdMax = clientMap.rbegin()->first;
+ }
+}
+
+
+//
+// Adjust the FDSets for a single given Client according to a new event Type mask.
+//
+void Selector::set(int fd, Type type)
+{
+ assert(fd >= 0);
+ inSet.set(fd, type & input);
+ outSet.set(fd, type & output);
+ errSet.set(fd, type & critical);
+ secdebug("selector", "fd %d notifications 0x%x", fd, type);
+}
+
+
+void Selector::operator () ()
+{
+ if (!clientMap.empty())
+ singleStep(0);
+}
+
+
+void Selector::operator () (Time::Absolute stopTime)
+{
+ if (!clientMap.empty())
+ singleStep(stopTime - Time::now());
+}
+
+
+//
+// Perform a single pass through the Selector and notify all clients
+// that have selected I/O pending at this time.
+// There is not time limit on how long this may take; if the clients
+// are well written, it won't be too long.
+//
+void Selector::singleStep(Time::Interval maxWait)
+{
+ assert(!clientMap.empty());
+ secdebug("selector", "select(%d) [%d-%d] for %ld clients",
+ fdMax + 1, fdMin, fdMax, clientMap.size());
+ for (;;) { // pseudo-loop - only retries
+ struct timeval duration = maxWait.timevalInterval();
+#if defined(__APPLE__)
+ // ad-hoc fix: MacOS X's BSD rejects times of more than 100E6 seconds
+ if (duration.tv_sec > 100000000)
+ duration.tv_sec = 100000000;
+#endif
+ const int size = FDSet::words(fdMax); // number of active words in sets
+ switch (int hits = ::select(fdMax + 1,
+ inSet.make(size), outSet.make(size), errSet.make(size),
+ &duration)) {
+ case -1: // error
+ if (errno == EINTR)
+ continue;
+ secdebug("selector", "select failed: errno=%d", errno);
+ UnixError::throwMe();
+ case 0: // no events
+ secdebug("selector", "select returned nothing");
+ return;
+ default: // some events
+ secdebug("selector", "%d pending descriptors", hits);
+ //@@@ This could be optimized as a word-merge scan.
+ //@@@ The typical case doesn't benefit from this though, though browsers might
+ //@@@ and integrated servers definitely would.
+ for (int fd = fdMin; fd <= fdMax && hits > 0; fd++) {
+ int types = 0;
+ if (inSet[fd]) types |= input;
+ if (outSet[fd]) types |= output;
+ if (errSet[fd]) types |= critical;
+ if (types) {
+ secdebug("selector", "notify fd %d client %p type %d",
+ fd, clientMap[fd], types);
+ clientMap[fd]->notify(fd, types);
+ hits--;
+ }
+ }
+ return;
+ }
+ }
+}
+
+
+} // end namespace IPPlusPlus
+} // end namespace Security