#include "selector.h"
#include <security_utilities/errors.h>
#include <security_utilities/debugging.h>
#include <algorithm> // min/max
namespace Security {
namespace UnixPlusPlus {
Selector::Selector() : fdMin(INT_MAX), fdMax(-1)
{
fdSetSize = FD_SETSIZE / NFDBITS;
inSet.grow(0, fdSetSize);
outSet.grow(0, fdSetSize);
errSet.grow(0, fdSetSize);
}
Selector::~Selector()
{ }
void Selector::add(int fd, Client &client, Type type)
{
assert(!client.isActive()); assert(fd >= 0);
secinfo("selector", "add client %p fd %d type=%d", &client, fd, type);
unsigned int pos = fd / NFDBITS;
if (pos >= fdSetSize) {
int newSize = (fd - 1) / NFDBITS + 2; inSet.grow(fdSetSize, newSize);
outSet.grow(fdSetSize, newSize);
errSet.grow(fdSetSize, newSize);
}
if (fd < fdMin)
fdMin = fd;
if (fd > fdMax)
fdMax = fd;
Client * &slot = clientMap[fd];
assert(!slot);
slot = &client;
client.mFd = fd;
client.mSelector = this;
client.mEvents = type;
set(fd, type);
}
void Selector::remove(int fd)
{
assert(fd >= 0);
ClientMap::iterator it = clientMap.find(fd);
assert(it != clientMap.end());
assert(it->second->mSelector == this);
secinfo("selector", "remove client %p fd %d", it->second, fd);
set(fd, none);
it->second->mSelector = NULL;
clientMap.erase(it);
if (isEmpty()) {
fdMin = INT_MAX;
fdMax = -1;
} else if (fd == fdMin) {
fdMin = clientMap.begin()->first;
} else if (fd == fdMax) {
fdMax = clientMap.rbegin()->first;
}
}
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);
secinfo("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());
}
void Selector::singleStep(Time::Interval maxWait)
{
assert(!clientMap.empty());
secinfo("selector", "select(%d) [%d-%d] for %ld clients",
fdMax + 1, fdMin, fdMax, clientMap.size());
for (;;) { struct timeval duration = maxWait.timevalInterval();
#if defined(__APPLE__)
if (duration.tv_sec > 100000000)
duration.tv_sec = 100000000;
#endif
const int size = FDSet::words(fdMax); switch (int hits = ::select(fdMax + 1,
inSet.make(size), outSet.make(size), errSet.make(size),
&duration)) {
case -1: if (errno == EINTR)
continue;
secinfo("selector", "select failed: errno=%d", errno);
UnixError::throwMe();
case 0: secinfo("selector", "select returned nothing");
return;
default: secinfo("selector", "%d pending descriptors", hits);
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) {
secinfo("selector", "notify fd %d client %p type %d",
fd, clientMap[fd], types);
clientMap[fd]->notify(fd, types);
hits--;
}
}
return;
}
}
}
} }