]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
socketengine_select Reset the values in the timeval struct on *nix before select()
[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_config.h"
22
23 #include "inspircd.h"
24 #include "socketengine.h"
25
26 #ifndef _WIN32
27 #include <sys/select.h>
28 #endif // _WIN32
29
30 /** A specialisation of the SocketEngine class, designed to use traditional select().
31  */
32 class SelectEngine : public SocketEngine
33 {
34         fd_set ReadSet, WriteSet, ErrSet;
35         int MaxFD;
36
37 public:
38         /** Create a new SelectEngine
39          */
40         SelectEngine();
41         /** Delete a SelectEngine
42          */
43         virtual ~SelectEngine();
44         virtual bool AddFd(EventHandler* eh, int event_mask);
45         virtual void DelFd(EventHandler* eh);
46         void OnSetEvent(EventHandler* eh, int, int);
47         virtual int DispatchEvents();
48         virtual std::string GetName();
49 };
50
51 SelectEngine::SelectEngine()
52 {
53         MAX_DESCRIPTORS = FD_SETSIZE;
54         CurrentSetSize = 0;
55
56         ref = new EventHandler* [GetMaxFds()];
57         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
58
59         FD_ZERO(&ReadSet);
60         FD_ZERO(&WriteSet);
61         FD_ZERO(&ErrSet);
62         MaxFD = 0;
63 }
64
65 SelectEngine::~SelectEngine()
66 {
67         delete[] ref;
68 }
69
70 bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
71 {
72         int fd = eh->GetFd();
73         if ((fd < 0) || (fd > GetMaxFds() - 1))
74                 return false;
75
76         if (ref[fd])
77                 return false;
78
79         ref[fd] = eh;
80
81         SocketEngine::SetEventMask(eh, event_mask);
82         OnSetEvent(eh, 0, event_mask);
83         FD_SET(fd, &ErrSet);
84         if (fd > MaxFD)
85                 MaxFD = fd;
86
87         CurrentSetSize++;
88
89         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
90         return true;
91 }
92
93 void SelectEngine::DelFd(EventHandler* eh)
94 {
95         int fd = eh->GetFd();
96
97         if ((fd < 0) || (fd > GetMaxFds() - 1))
98                 return;
99
100         CurrentSetSize--;
101         ref[fd] = NULL;
102
103         FD_CLR(fd, &ReadSet);
104         FD_CLR(fd, &WriteSet);
105         FD_CLR(fd, &ErrSet);
106         if (fd == MaxFD)
107                 --MaxFD;
108
109         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
110 }
111
112 void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
113 {
114         int fd = eh->GetFd();
115         int diff = old_mask ^ new_mask;
116
117         if (diff & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
118         {
119                 if (new_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
120                         FD_SET(fd, &ReadSet);
121                 else
122                         FD_CLR(fd, &ReadSet);
123         }
124         if (diff & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
125         {
126                 if (new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
127                         FD_SET(fd, &WriteSet);
128                 else
129                         FD_CLR(fd, &WriteSet);
130         }
131 }
132
133 int SelectEngine::DispatchEvents()
134 {
135         timeval tval;
136         tval.tv_sec = 1;
137         tval.tv_usec = 0;
138
139         fd_set rfdset = ReadSet, wfdset = WriteSet, errfdset = ErrSet;
140
141         int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
142         ServerInstance->UpdateTime();
143
144         /* Nothing to process this time around */
145         if (sresult < 1)
146                 return 0;
147
148         for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
149         {
150                 int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
151
152                 if (has_read || has_write || has_error)
153                 {
154                         --j;
155
156                         EventHandler* ev = ref[i];
157                         if (!ev)
158                                 continue;
159
160                         if (has_error)
161                         {
162                                 ErrorEvents++;
163
164                                 socklen_t codesize = sizeof(int);
165                                 int errcode = 0;
166                                 if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
167                                         errcode = errno;
168
169                                 ev->HandleEvent(EVENT_ERROR, errcode);
170                                 continue;
171                         }
172
173                         if (has_read)
174                         {
175                                 ReadEvents++;
176                                 SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
177                                 ev->HandleEvent(EVENT_READ);
178                                 if (ev != ref[i])
179                                         continue;
180                         }
181                         if (has_write)
182                         {
183                                 WriteEvents++;
184                                 int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
185                                 this->OnSetEvent(ev, ev->GetEventMask(), newmask);
186                                 SetEventMask(ev, newmask);
187                                 ev->HandleEvent(EVENT_WRITE);
188                         }
189                 }
190         }
191
192         return sresult;
193 }
194
195 std::string SelectEngine::GetName()
196 {
197         return "select";
198 }
199
200 SocketEngine* CreateSocketEngine()
201 {
202         return new SelectEngine;
203 }