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