diff options
Diffstat (limited to 'src/socketengines/select.cpp')
-rw-r--r-- | src/socketengines/select.cpp | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/src/socketengines/select.cpp b/src/socketengines/select.cpp new file mode 100644 index 000000000..6e02062db --- /dev/null +++ b/src/socketengines/select.cpp @@ -0,0 +1,145 @@ +/* + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +#include "services.h" +#include "anope.h" +#include "sockets.h" +#include "socketengine.h" +#include "logger.h" +#include "config.h" + +#include <thread> + +static int MaxFD; +static unsigned FDCount; +static fd_set ReadFDs; +static fd_set WriteFDs; + +void SocketEngine::Init() +{ + FD_ZERO(&ReadFDs); + FD_ZERO(&WriteFDs); +} + +void SocketEngine::Shutdown() +{ + while (!Sockets.empty()) + delete Sockets.begin()->second; +} + +void SocketEngine::Change(Socket *s, bool set, SocketFlag flag) +{ + if (set == s->flags[flag]) + return; + + bool before_registered = s->flags[SF_READABLE] || s->flags[SF_WRITABLE]; + + s->flags[flag] = set; + + bool now_registered = s->flags[SF_READABLE] || s->flags[SF_WRITABLE]; + + if (!before_registered && now_registered) + { + if (s->GetFD() > MaxFD) + MaxFD = s->GetFD(); + if (s->flags[SF_READABLE]) + FD_SET(s->GetFD(), &ReadFDs); + if (s->flags[SF_WRITABLE]) + FD_SET(s->GetFD(), &WriteFDs); + ++FDCount; + } + else if (before_registered && !now_registered) + { + if (s->GetFD() == MaxFD) + --MaxFD; + FD_CLR(s->GetFD(), &ReadFDs); + FD_CLR(s->GetFD(), &WriteFDs); + --FDCount; + } + else if (before_registered && now_registered) + { + if (s->flags[SF_READABLE]) + FD_SET(s->GetFD(), &ReadFDs); + else + FD_CLR(s->GetFD(), &ReadFDs); + + if (s->flags[SF_WRITABLE]) + FD_SET(s->GetFD(), &WriteFDs); + else + FD_CLR(s->GetFD(), &WriteFDs); + } +} + +void SocketEngine::Process() +{ + fd_set rfdset = ReadFDs, wfdset = WriteFDs, efdset = ReadFDs; + timeval tval; + tval.tv_sec = Config->ReadTimeout; + tval.tv_usec = 0; + +#ifdef _WIN32 + /* We can use the socket engine to "sleep" services for a period of + * time between connections to the uplink, which allows modules, + * timers, etc to function properly. Windows, being as useful as it is, + * does not allow to select() on 0 sockets and will immediately return error. + * Thus: + */ + if (FDCount == 0) + { + std::this_thread::sleep_for(std::chrono::seconds(tval.tv_sec)); + return; + } +#endif + + int sresult = select(MaxFD + 1, &rfdset, &wfdset, &efdset, &tval); + Anope::UpdateTime(); + + if (sresult == -1) + { + Log() << "SockEngine::Process(): error: " << Anope::LastError(); + } + else if (sresult) + { + int processed = 0; + for (std::map<int, Socket *>::const_iterator it = Sockets.begin(), it_end = Sockets.end(); it != it_end && processed != sresult;) + { + Socket *s = it->second; + ++it; + + bool has_read = FD_ISSET(s->GetFD(), &rfdset), has_write = FD_ISSET(s->GetFD(), &wfdset), has_error = FD_ISSET(s->GetFD(), &efdset); + if (has_read || has_write || has_error) + ++processed; + + if (has_error) + { + s->ProcessError(); + delete s; + continue; + } + + if (!s->Process()) + { + if (s->flags[SF_DEAD]) + delete s; + continue; + } + + if (has_read && !s->ProcessRead()) + s->flags[SF_DEAD] = true; + + if (has_write && !s->ProcessWrite()) + s->flags[SF_DEAD] = true; + + if (s->flags[SF_DEAD]) + delete s; + } + } +} |