]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
Replace copyright headers with headers granting specific authors copyright
[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 WINDOWS
27 #include <sys/select.h>
28 #endif // WINDOWS
29
30 /** A specialisation of the SocketEngine class, designed to use traditional select().
31  */
32 class SelectEngine : public SocketEngine
33 {
34 public:
35         /** Create a new SelectEngine
36          */
37         SelectEngine();
38         /** Delete a SelectEngine
39          */
40         virtual ~SelectEngine();
41         virtual bool AddFd(EventHandler* eh, int event_mask);
42         virtual void DelFd(EventHandler* eh);
43         void OnSetEvent(EventHandler* eh, int, int);
44         virtual int DispatchEvents();
45         virtual std::string GetName();
46 };
47
48 SelectEngine::SelectEngine()
49 {
50         MAX_DESCRIPTORS = FD_SETSIZE;
51         CurrentSetSize = 0;
52
53         ref = new EventHandler* [GetMaxFds()];
54         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
55 }
56
57 SelectEngine::~SelectEngine()
58 {
59         delete[] ref;
60 }
61
62 bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
63 {
64         int fd = eh->GetFd();
65         if ((fd < 0) || (fd > GetMaxFds() - 1))
66                 return false;
67
68         if (ref[fd])
69                 return false;
70
71         ref[fd] = eh;
72         SocketEngine::SetEventMask(eh, event_mask);
73         CurrentSetSize++;
74
75         ServerInstance->Logs->Log("SOCKET",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         ref[fd] = NULL;
88
89         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
90 }
91
92 void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
93 {
94         // deal with it later
95 }
96
97 int SelectEngine::DispatchEvents()
98 {
99         timeval tval;
100         int sresult = 0;
101         socklen_t codesize = sizeof(int);
102         int errcode = 0;
103
104         fd_set wfdset, rfdset, errfdset;
105         FD_ZERO(&wfdset);
106         FD_ZERO(&rfdset);
107         FD_ZERO(&errfdset);
108
109         /* Populate the select FD sets (this is why select sucks compared to epoll, kqueue) */
110         for (unsigned int i = 0; i < FD_SETSIZE; i++)
111         {
112                 EventHandler* eh = ref[i];
113                 if (!eh)
114                         continue;
115                 int state = eh->GetEventMask();
116                 if (state & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
117                         FD_SET (i, &rfdset);
118                 if (state & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
119                         FD_SET (i, &wfdset);
120                 FD_SET (i, &errfdset);
121         }
122
123         /* One second wait */
124         tval.tv_sec = 1;
125         tval.tv_usec = 0;
126
127         sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval);
128         ServerInstance->UpdateTime();
129
130         /* Nothing to process this time around */
131         if (sresult < 1)
132                 return 0;
133
134         for (int i = 0; i < FD_SETSIZE; i++)
135         {
136                 EventHandler* ev = ref[i];
137                 if (ev)
138                 {
139                         if (FD_ISSET (i, &errfdset))
140                         {
141                                 ErrorEvents++;
142                                 if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
143                                         errcode = errno;
144
145                                 ev->HandleEvent(EVENT_ERROR, errcode);
146                                 continue;
147                         }
148                         else
149                         {
150                                 /* NOTE: This is a pair of seperate if statements as the socket
151                                  * may be in both read and writeable state at the same time.
152                                  * If an error event occurs above it is not worth processing the
153                                  * read and write states even if set.
154                                  */
155                                 if (FD_ISSET (i, &rfdset))
156                                 {
157                                         ReadEvents++;
158                                         SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
159                                         ev->HandleEvent(EVENT_READ);
160                                         if (ev != ref[i])
161                                                 continue;
162                                 }
163                                 if (FD_ISSET (i, &wfdset))
164                                 {
165                                         WriteEvents++;
166                                         SetEventMask(ev, ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
167                                         ev->HandleEvent(EVENT_WRITE);
168                                 }
169                         }
170                 }
171         }
172
173         return sresult;
174 }
175
176 std::string SelectEngine::GetName()
177 {
178         return "select";
179 }
180
181 SocketEngine* CreateSocketEngine()
182 {
183         return new SelectEngine;
184 }