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