]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengine_kqueue.cpp
Move whowas containers into whowas class to avoid all cpp files including cmd_whowas...
[user/henk/code/inspircd.git] / src / socketengine_kqueue.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 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 #include "exitcodes.h"
16 #include <sys/types.h>
17 #include <sys/event.h>
18 #include <sys/time.h>
19 #include "socketengine_kqueue.h"
20
21
22 KQueueEngine::KQueueEngine(InspIRCd* Instance) : SocketEngine(Instance)
23 {
24         EngineHandle = kqueue();
25         if (EngineHandle == -1)
26         {
27                 ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
28                 ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now.");
29                 printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
30                 printf("ERROR: this is a fatal error, exiting now.");
31                 InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
32         }
33         CurrentSetSize = 0;
34 }
35
36 KQueueEngine::~KQueueEngine()
37 {
38         ServerInstance->Log(DEBUG,"KQueueEngine::~KQueueEngine()");
39         close(EngineHandle);
40 }
41
42 bool KQueueEngine::AddFd(EventHandler* eh)
43 {
44         int fd = eh->GetFd();
45
46         ServerInstance->Log(DEBUG,"KQueueEngine::AddFd(%d)",fd);
47
48         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
49         {
50                 ServerInstance->Log(DEBUG,"ERROR: FD of %d added above max of %d",fd,MAX_DESCRIPTORS);
51                 return false;
52         }
53         if (GetRemainingFds() <= 1)
54         {
55                 ServerInstance->Log(DEBUG,"ERROR: System out of file descriptors!");
56                 return false;
57         }
58
59         if (ref[fd])
60         {
61                 ServerInstance->Log(DEBUG,"ERROR: Slot already occupied");
62                 return false;
63         }
64
65         ref[fd] = eh;
66         ServerInstance->Log(DEBUG,"Add socket %d",fd);
67
68         struct kevent ke;
69         ServerInstance->Log(DEBUG,"kqueue: Add socket to events, kq=%d socket=%d",EngineHandle,fd);
70         EV_SET(&ke, fd, eh->Readable() ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL);
71
72         int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
73         if (i == -1)
74         {
75                 ServerInstance->Log(DEBUG,"kqueue: List insertion failure!");
76                 return false;
77         }
78
79         CurrentSetSize++;
80         return true;
81 }
82
83 bool KQueueEngine::DelFd(EventHandler* eh)
84 {
85         int fd = eh->GetFd();
86
87         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
88                 return false;
89
90         struct kevent ke;
91         EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
92
93         int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
94         
95         EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
96
97         int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
98
99         if ((j < 0) && (i < 0))
100                 return false;
101
102         CurrentSetSize--;
103         ref[fd] = NULL;
104
105         return true;
106 }
107
108 void KQueueEngine::WantWrite(EventHandler* eh)
109 {
110         /** When changing an item in a kqueue, there is no 'modify' call
111          * as in epoll. Instead, we add the item again, and this overwrites
112          * the original setting rather than adding it twice. See man kqueue.
113          */
114         struct kevent ke;
115         EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
116         int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
117         if (i == -1)
118         {
119                 ServerInstance->Log(DEBUG,"kqueue: Unable to set fd %d for wanting write", eh->GetFd());
120         }
121 }
122
123 int KQueueEngine::GetMaxFds()
124 {
125         return MAX_DESCRIPTORS;
126 }
127
128 int KQueueEngine::GetRemainingFds()
129 {
130         return MAX_DESCRIPTORS - CurrentSetSize;
131 }
132
133 int KQueueEngine::DispatchEvents()
134 {
135         ts.tv_nsec = 0;
136         ts.tv_sec = 1;
137         int i = kevent(EngineHandle, NULL, 0, &ke_list[0], MAX_DESCRIPTORS, &ts);
138         for (int j = 0; j < i; j++)
139         {
140                 if (ke_list[j].flags & EV_EOF)
141                 {
142                         ServerInstance->Log(DEBUG,"kqueue: Error on FD %d", ke_list[j].ident);
143                         /* We love you kqueue, oh yes we do *sings*!
144                          * kqueue gives us the error number directly in the EOF state!
145                          * Unlike smelly epoll and select, where we have to getsockopt
146                          * to get the error, this saves us time and cpu cycles. Go BSD!
147                          */
148                         if (ref[ke_list[j].ident])
149                                 ref[ke_list[j].ident]->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
150                         continue;
151                 }
152                 if (ke_list[j].flags & EVFILT_WRITE)
153                 {
154                         /* This looks wrong but its right. As above, theres no modify 
155                          * call in kqueue. See the manpage.
156                          */
157                         struct kevent ke;
158                         EV_SET(&ke, ke_list[j].ident, EVFILT_READ, EV_ADD, 0, 0, NULL);
159                         int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
160                         if (i == -1)
161                         {
162                                 ServerInstance->Log(DEBUG,"kqueue: Unable to set fd %d back to just wanting to read!", ke_list[j].ident);
163                         }
164                         if (ref[ke_list[j].ident])
165                                 ref[ke_list[j].ident]->HandleEvent(EVENT_WRITE);
166                 }
167                 else
168                 {
169                         if (ref[ke_list[j].ident])
170                                 ref[ke_list[j].ident]->HandleEvent(EVENT_READ);
171                 }
172         }
173
174         return i;
175 }
176
177 std::string KQueueEngine::GetName()
178 {
179         return "kqueue";
180 }