]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
f52fb08b8b4e1cd5cd801607eaa13c77c28a7d29
[user/henk/code/inspircd.git] / src / socketengines / socketengine_select.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd_config.h"
15
16 #include "inspircd.h"
17 #include "socketengine.h"
18
19 #ifndef WINDOWS
20 #include <sys/select.h>
21 #endif // WINDOWS
22
23 /** A specialisation of the SocketEngine class, designed to use traditional select().
24  */
25 class SelectEngine : public SocketEngine
26 {
27 public:
28         /** Create a new SelectEngine
29          */
30         SelectEngine();
31         /** Delete a SelectEngine
32          */
33         virtual ~SelectEngine();
34         virtual bool AddFd(EventHandler* eh, int event_mask);
35         virtual bool DelFd(EventHandler* eh, bool force = false);
36         void OnSetEvent(EventHandler* eh, int, int);
37         virtual int DispatchEvents();
38         virtual std::string GetName();
39 };
40
41 SelectEngine::SelectEngine()
42 {
43         MAX_DESCRIPTORS = FD_SETSIZE;
44         CurrentSetSize = 0;
45
46         ref = new EventHandler* [GetMaxFds()];
47         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
48 }
49
50 SelectEngine::~SelectEngine()
51 {
52         delete[] ref;
53 }
54
55 bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
56 {
57         int fd = eh->GetFd();
58         if ((fd < 0) || (fd > GetMaxFds() - 1))
59                 return false;
60
61         if (ref[fd])
62                 return false;
63
64         ref[fd] = eh;
65         SocketEngine::SetEventMask(eh, event_mask);
66         CurrentSetSize++;
67
68         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
69         return true;
70 }
71
72 bool SelectEngine::DelFd(EventHandler* eh, bool force)
73 {
74         int fd = eh->GetFd();
75
76         if ((fd < 0) || (fd > GetMaxFds() - 1))
77                 return false;
78
79         CurrentSetSize--;
80         ref[fd] = NULL;
81
82         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
83         return true;
84 }
85
86 void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
87 {
88         // deal with it later
89 }
90
91 int SelectEngine::DispatchEvents()
92 {
93         timeval tval;
94         int sresult = 0;
95         socklen_t codesize = sizeof(int);
96         int errcode = 0;
97
98         fd_set wfdset, rfdset, errfdset;
99         FD_ZERO(&wfdset);
100         FD_ZERO(&rfdset);
101         FD_ZERO(&errfdset);
102
103         /* Populate the select FD sets (this is why select sucks compared to epoll, kqueue) */
104         for (unsigned int i = 0; i < FD_SETSIZE; i++)
105         {
106                 EventHandler* eh = ref[i];
107                 if (!eh)
108                         continue;
109                 int state = eh->GetEventMask();
110                 if (state & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
111                         FD_SET (i, &rfdset);
112                 if (state & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
113                         FD_SET (i, &wfdset);
114                 FD_SET (i, &errfdset);
115         }
116
117         /* One second wait */
118         tval.tv_sec = 1;
119         tval.tv_usec = 0;
120
121         sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval);
122
123         /* Nothing to process this time around */
124         if (sresult < 1)
125                 return 0;
126
127         for (int i = 0; i < FD_SETSIZE; i++)
128         {
129                 EventHandler* ev = ref[i];
130                 if (ev)
131                 {
132                         if (FD_ISSET (i, &errfdset))
133                         {
134                                 ErrorEvents++;
135                                 if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
136                                         errcode = errno;
137
138                                 ev->HandleEvent(EVENT_ERROR, errcode);
139                                 continue;
140                         }
141                         else
142                         {
143                                 /* NOTE: This is a pair of seperate if statements as the socket
144                                  * may be in both read and writeable state at the same time.
145                                  * If an error event occurs above it is not worth processing the
146                                  * read and write states even if set.
147                                  */
148                                 if (FD_ISSET (i, &rfdset))
149                                 {
150                                         ReadEvents++;
151                                         SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
152                                         ev->HandleEvent(EVENT_READ);
153                                 }
154                                 if (FD_ISSET (i, &wfdset))
155                                 {
156                                         WriteEvents++;
157                                         SetEventMask(ev, ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
158                                         ev->HandleEvent(EVENT_WRITE);
159                                 }
160                         }
161                 }
162         }
163
164         return sresult;
165 }
166
167 std::string SelectEngine::GetName()
168 {
169         return "select";
170 }
171
172 SocketEngine* CreateSocketEngine()
173 {
174         return new SelectEngine;
175 }