]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
Clean up SocketEngine interface to allow edge-triggered I/O and sockets that do not...
[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.h"
15 #ifndef WINDOWS
16 #include <sys/select.h>
17 #endif // WINDOWS
18 #include "socketengines/socketengine_select.h"
19
20
21 SelectEngine::SelectEngine()
22 {
23         MAX_DESCRIPTORS = FD_SETSIZE;
24         CurrentSetSize = 0;
25
26         ref = new EventHandler* [GetMaxFds()];
27         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
28 }
29
30 SelectEngine::~SelectEngine()
31 {
32         delete[] ref;
33 }
34
35 bool SelectEngine::AddFd(EventHandler* eh, int)
36 {
37         int fd = eh->GetFd();
38         if ((fd < 0) || (fd > GetMaxFds() - 1))
39                 return false;
40
41         if (ref[fd])
42                 return false;
43
44         ref[fd] = eh;
45         SocketEngine::SetEventMask(eh, event_mask);
46         CurrentSetSize++;
47
48         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
49         return true;
50 }
51
52 bool SelectEngine::DelFd(EventHandler* eh, bool force)
53 {
54         int fd = eh->GetFd();
55
56         if ((fd < 0) || (fd > GetMaxFds() - 1))
57                 return false;
58
59         CurrentSetSize--;
60         ref[fd] = NULL;
61
62         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
63         return true;
64 }
65
66 void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
67 {
68         // deal with it later
69 }
70
71 int SelectEngine::DispatchEvents()
72 {
73         timeval tval;
74         int sresult = 0;
75         socklen_t codesize = sizeof(int);
76         int errcode = 0;
77
78         fd_set wfdset, rfdset, errfdset;
79         FD_ZERO(&wfdset);
80         FD_ZERO(&rfdset);
81         FD_ZERO(&errfdset);
82
83         /* Populate the select FD sets (this is why select sucks compared to epoll, kqueue, IOCP) */
84         for (int i = 0; i < FD_SETSIZE; i++)
85         {
86                 EventHandler* eh = ref[i];
87                 if (!eh)
88                         continue;
89                 int state = eh->GetEventMask();
90                 if (state & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
91                         FD_SET (i, &rfdset);
92                 if (state & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE))
93                         FD_SET (i, &wfdset);
94                 FD_SET (i, &errfdset);
95         }
96
97         /* One second wait */
98         tval.tv_sec = 1;
99         tval.tv_usec = 0;
100
101         sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval);
102
103         /* Nothing to process this time around */
104         if (sresult < 1)
105                 return 0;
106
107         for (int i = 0; i < FD_SETSIZE; i++)
108         {
109                 EventHandler* ev = ref[i];
110                 if (ev)
111                 {
112                         if (FD_ISSET (i, &errfdset))
113                         {
114                                 ErrorEvents++;
115                                 if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
116                                         errcode = errno;
117
118                                 ev->HandleEvent(EVENT_ERROR, errcode);
119                                 continue;
120                         }
121                         else
122                         {
123                                 /* NOTE: This is a pair of seperate if statements as the socket
124                                  * may be in both read and writeable state at the same time.
125                                  * If an error event occurs above it is not worth processing the
126                                  * read and write states even if set.
127                                  */
128                                 if (FD_ISSET (i, &rfdset))
129                                 {
130                                         ReadEvents++;
131                                         SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
132                                         ev->HandleEvent(EVENT_READ);
133                                 }
134                                 if (FD_ISSET (i, &wfdset))
135                                 {
136                                         WriteEvents++;
137                                         SetEventMask(eh, eh->GetEventMask() & ~FD_WRITE_WILL_BLOCK);
138                                         ev->HandleEvent(EVENT_WRITE);
139                                 }
140                         }
141                 }
142         }
143
144         return sresult;
145 }
146
147 std::string SelectEngine::GetName()
148 {
149         return "select";
150 }