/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "inspircd_config.h"
#include "inspircd.h"
#include "socketengine.h"
#ifndef _WIN32
#include <sys/select.h>
#endif // _WIN32
/** A specialisation of the SocketEngine class, designed to use traditional select().
*/
class SelectEngine : public SocketEngine
{
fd_set ReadSet, WriteSet, ErrSet;
int MaxFD;
public:
/** Create a new SelectEngine
*/
SelectEngine();
/** Delete a SelectEngine
*/
virtual ~SelectEngine();
virtual bool AddFd(EventHandler* eh, int event_mask);
virtual void DelFd(EventHandler* eh);
void OnSetEvent(EventHandler* eh, int, int);
virtual int DispatchEvents();
virtual std::string GetName();
};
SelectEngine::SelectEngine()
{
MAX_DESCRIPTORS = FD_SETSIZE;
CurrentSetSize = 0;
ref = new EventHandler* [GetMaxFds()];
memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
FD_ZERO(&ReadSet);
FD_ZERO(&WriteSet);
FD_ZERO(&ErrSet);
MaxFD = 0;
}
SelectEngine::~SelectEngine()
{
delete[] ref;
}
bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
return false;
if (ref[fd])
return false;
ref[fd] = eh;
SocketEngine::SetEventMask(eh, event_mask);
OnSetEvent(eh, 0, event_mask);
FD_SET(fd, &ErrSet);
if (fd > MaxFD)
MaxFD = fd;
CurrentSetSize++;
ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
return true;
}
void SelectEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
return;
CurrentSetSize--;
ref[fd] = NULL;
FD_CLR(fd, &ReadSet);
FD_CLR(fd, &WriteSet);
FD_CLR(fd, &ErrSet);
if (fd == MaxFD)
--MaxFD;
ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
}
void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
int fd = eh->GetFd();
int diff = old_mask ^ new_mask;
if (diff & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
{
if (new_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
FD_SET(fd, &ReadSet);
else
FD_CLR(fd, &ReadSet);
}
if (diff & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
{
if (new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
FD_SET(fd, &WriteSet);
else
FD_CLR(fd, &WriteSet);
}
}
int SelectEngine::DispatchEvents()
{
static timeval tval = { 1, 0 };
fd_set rfdset = ReadSet, wfdset = WriteSet, errfdset = ErrSet;
int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
ServerInstance->UpdateTime();
/* Nothing to process this time around */
if (sresult < 1)
return 0;
for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
{
int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
if (has_read || has_write || has_error)
{
--j;
EventHandler* ev = ref[i];
if (!ev)
continue;
if (has_error)
{
ErrorEvents++;
socklen_t codesize = sizeof(int);
int errcode = 0;
if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
errcode = errno;
ev->HandleEvent(EVENT_ERROR, errcode);
continue;
}
if (has_read)
{
ReadEvents++;
SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
ev->HandleEvent(EVENT_READ);
if (ev != ref[i])
continue;
}
if (has_write)
{
WriteEvents++;
int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
this->OnSetEvent(ev, ev->GetEventMask(), newmask);
SetEventMask(ev, newmask);
ev->HandleEvent(EVENT_WRITE);
}
}
}
return sresult;
}
std::string SelectEngine::GetName()
{
return "select";
}
SocketEngine* CreateSocketEngine()
{
return new SelectEngine;
}