]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
New socketengine stuff:
[user/henk/code/inspircd.git] / src / socketengines / socketengine_select.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2014 Adam <Adam@anope.org>
5  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
6  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
7  *
8  * This file is part of InspIRCd.  InspIRCd is free software: you can
9  * redistribute it and/or modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation, version 2.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include "inspircd.h"
23 #include "socketengine.h"
24
25 #ifndef _WIN32
26 #include <sys/select.h>
27 #endif // _WIN32
28
29 /** A specialisation of the SocketEngine class, designed to use traditional select().
30  */
31 class SelectEngine : public SocketEngine
32 {
33         fd_set ReadSet, WriteSet, ErrSet;
34         int MaxFD;
35
36 public:
37         /** Create a new SelectEngine
38          */
39         SelectEngine();
40         virtual bool AddFd(EventHandler* eh, int event_mask);
41         virtual void DelFd(EventHandler* eh);
42         void OnSetEvent(EventHandler* eh, int, int);
43         virtual int DispatchEvents();
44         virtual std::string GetName();
45 };
46
47 SelectEngine::SelectEngine()
48 {
49         MAX_DESCRIPTORS = FD_SETSIZE;
50         CurrentSetSize = 0;
51
52         FD_ZERO(&ReadSet);
53         FD_ZERO(&WriteSet);
54         FD_ZERO(&ErrSet);
55         MaxFD = 0;
56 }
57
58 bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
59 {
60         int fd = eh->GetFd();
61         if ((fd < 0) || (fd > GetMaxFds() - 1))
62                 return false;
63
64         if (!SocketEngine::AddFd(eh))
65                 return false;
66
67         SocketEngine::SetEventMask(eh, event_mask);
68         OnSetEvent(eh, 0, event_mask);
69         FD_SET(fd, &ErrSet);
70         if (fd > MaxFD)
71                 MaxFD = fd;
72
73         CurrentSetSize++;
74
75         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
76         return true;
77 }
78
79 void SelectEngine::DelFd(EventHandler* eh)
80 {
81         int fd = eh->GetFd();
82
83         if ((fd < 0) || (fd > GetMaxFds() - 1))
84                 return;
85
86         CurrentSetSize--;
87         SocketEngine::DelFd(eh);
88
89         FD_CLR(fd, &ReadSet);
90         FD_CLR(fd, &WriteSet);
91         FD_CLR(fd, &ErrSet);
92         if (fd == MaxFD)
93                 --MaxFD;
94
95         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
96 }
97
98 void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
99 {
100         int fd = eh->GetFd();
101         int diff = old_mask ^ new_mask;
102
103         if (diff & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
104         {
105                 if (new_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
106                         FD_SET(fd, &ReadSet);
107                 else
108                         FD_CLR(fd, &ReadSet);
109         }
110         if (diff & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
111         {
112                 if (new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
113                         FD_SET(fd, &WriteSet);
114                 else
115                         FD_CLR(fd, &WriteSet);
116         }
117 }
118
119 int SelectEngine::DispatchEvents()
120 {
121         static timeval tval = { 1, 0 };
122
123         fd_set rfdset = ReadSet, wfdset = WriteSet, errfdset = ErrSet;
124
125         int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
126         ServerInstance->UpdateTime();
127
128         /* Nothing to process this time around */
129         if (sresult < 1)
130                 return 0;
131
132         for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
133         {
134                 int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
135
136                 if (has_read || has_write || has_error)
137                 {
138                         --j;
139
140                         EventHandler* ev = GetRef(i);
141                         if (!ev)
142                                 continue;
143
144                         if (has_error)
145                         {
146                                 ErrorEvents++;
147
148                                 socklen_t codesize = sizeof(int);
149                                 int errcode = 0;
150                                 if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
151                                         errcode = errno;
152
153                                 ev->HandleEvent(EVENT_ERROR, errcode);
154                                 continue;
155                         }
156
157                         if (has_read)
158                         {
159                                 ReadEvents++;
160                                 SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
161                                 ev->HandleEvent(EVENT_READ);
162                                 if (ev != GetRef(i))
163                                         continue;
164                         }
165                         if (has_write)
166                         {
167                                 WriteEvents++;
168                                 int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
169                                 this->OnSetEvent(ev, ev->GetEventMask(), newmask);
170                                 SetEventMask(ev, newmask);
171                                 ev->HandleEvent(EVENT_WRITE);
172                         }
173                 }
174         }
175
176         return sresult;
177 }
178
179 std::string SelectEngine::GetName()
180 {
181         return "select";
182 }
183
184 SocketEngine* CreateSocketEngine()
185 {
186         return new SelectEngine;
187 }