]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
795e844e6ac58cb4dd09c1ba848f79d038e10f28
[user/henk/code/inspircd.git] / src / socketengines / socketengine_select.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2010 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         ServerInstance->UpdateTime();
123
124         /* Nothing to process this time around */
125         if (sresult < 1)
126                 return 0;
127
128         for (int i = 0; i < FD_SETSIZE; i++)
129         {
130                 EventHandler* ev = ref[i];
131                 if (ev)
132                 {
133                         if (FD_ISSET (i, &errfdset))
134                         {
135                                 ErrorEvents++;
136                                 if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
137                                         errcode = errno;
138
139                                 ev->HandleEvent(EVENT_ERROR, errcode);
140                                 continue;
141                         }
142                         else
143                         {
144                                 /* NOTE: This is a pair of seperate if statements as the socket
145                                  * may be in both read and writeable state at the same time.
146                                  * If an error event occurs above it is not worth processing the
147                                  * read and write states even if set.
148                                  */
149                                 if (FD_ISSET (i, &rfdset))
150                                 {
151                                         ReadEvents++;
152                                         SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
153                                         ev->HandleEvent(EVENT_READ);
154                                 }
155                                 if (FD_ISSET (i, &wfdset))
156                                 {
157                                         WriteEvents++;
158                                         SetEventMask(ev, ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
159                                         ev->HandleEvent(EVENT_WRITE);
160                                 }
161                         }
162                 }
163         }
164
165         return sresult;
166 }
167
168 std::string SelectEngine::GetName()
169 {
170         return "select";
171 }
172
173 SocketEngine* CreateSocketEngine()
174 {
175         return new SelectEngine;
176 }