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