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