]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
Forward port r10234, thanks psychon
[user/henk/code/inspircd.git] / src / socketengines / socketengine_select.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/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(InspIRCd* Instance) : SocketEngine(Instance)
22 {
23         MAX_DESCRIPTORS = FD_SETSIZE;
24         EngineHandle = 0;
25         CurrentSetSize = 0;
26
27         writeable = new bool [GetMaxFds()];
28         memset(writeable, 0, sizeof(writeable));
29         ref = new EventHandler* [GetMaxFds()];
30         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
31 }
32
33 SelectEngine::~SelectEngine()
34 {
35         delete[] ref;
36 }
37
38 bool SelectEngine::AddFd(EventHandler* eh)
39 {
40         int fd = eh->GetFd();
41         if ((fd < 0) || (fd > GetMaxFds() - 1))
42                 return false;
43
44         if (GetRemainingFds() <= 1)
45                 return false;
46
47         if (ref[fd])
48                 return false;
49
50         fds[fd] = fd;
51         ref[fd] = eh;
52         CurrentSetSize++;
53
54         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
55         return true;
56 }
57
58 void SelectEngine::WantWrite(EventHandler* eh)
59 {
60         writeable[eh->GetFd()] = true;
61 }
62
63 bool SelectEngine::DelFd(EventHandler* eh, bool force)
64 {
65         int fd = eh->GetFd();
66
67         if ((fd < 0) || (fd > GetMaxFds() - 1))
68                 return false;
69
70         std::map<int,int>::iterator t = fds.find(fd);
71         if (t != fds.end())
72                 fds.erase(t);
73
74         CurrentSetSize--;
75         ref[fd] = NULL;
76
77         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
78         return true;
79 }
80
81 int SelectEngine::GetMaxFds()
82 {
83         return FD_SETSIZE;
84 }
85
86 int SelectEngine::GetRemainingFds()
87 {
88         return GetMaxFds() - CurrentSetSize;
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_ZERO(&wfdset);
99         FD_ZERO(&rfdset);
100         FD_ZERO(&errfdset);
101
102         /* Populate the select FD set (this is why select sucks compared to epoll, kqueue, IOCP) */
103         for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++)
104         {
105                 if (ref[a->second]->Readable())
106                         /* Read notifications */
107                         FD_SET (a->second, &rfdset);
108                 else
109                         /* Write notifications */
110                         FD_SET (a->second, &wfdset);
111
112                 /* Explicitly one-time writeable */
113                 if (writeable[a->second])
114                         FD_SET (a->second, &wfdset);
115
116                 /* All sockets must receive error notifications regardless */
117                 FD_SET (a->second, &errfdset);
118         }
119
120         /* One second waits */
121         tval.tv_sec = 1;
122         tval.tv_usec = 0;
123
124         sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval);
125
126         /* Nothing to process this time around */
127         if (sresult < 1)
128                 return 0;
129
130         /* Safe assumption (as of 1.1 anyway) that a socket can't remove itself from the list in the middle of the loop */
131         for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++)
132         {
133                 EventHandler* ev = ref[a->second];
134                 if (ev)
135                 {
136                         if (FD_ISSET (ev->GetFd(), &errfdset))
137                         {
138                                 ErrorEvents++;
139                                 if (getsockopt(ev->GetFd(), SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
140                                         errcode = errno;
141
142                                 ev->HandleEvent(EVENT_ERROR, errcode);
143                                 continue;
144                         }
145                         else
146                         {
147                                 /* NOTE: This is a pair of seperate if statements as the socket
148                                  * may be in both read and writeable state at the same time.
149                                  * If an error event occurs above it is not worth processing the
150                                  * read and write states even if set.
151                                  */
152                                 if (FD_ISSET (ev->GetFd(), &wfdset))
153                                 {
154                                         WriteEvents++;
155                                         writeable[ev->GetFd()] = false;
156                                         ev->HandleEvent(EVENT_WRITE);
157                                 }
158                                 if (FD_ISSET (ev->GetFd(), &rfdset))
159                                 {
160                                                 ReadEvents++;
161                                                 ev->HandleEvent(EVENT_READ);
162                                 }
163                         }
164                 }
165         }
166
167         return sresult;
168 }
169
170 std::string SelectEngine::GetName()
171 {
172         return "select";
173 }