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