]> jfr.im git - irc/quakenet/newserv.git/blob - core/events-kqueue.c
merge
[irc/quakenet/newserv.git] / core / events-kqueue.c
1 /*
2 * events.c: the event handling core, kqueue() version
3 */
4
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/event.h>
8 #include <sys/time.h>
9 #include <sys/poll.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include "../lib/array.h"
13 #include "events.h"
14 #include "error.h"
15 #include "hooks.h"
16
17 /*
18 * OK, for the kqueue() version we just keep an array (indexed by fd)
19 * of what we put in the kqueue() so we can remove it later. This is only
20 * required because the deregisterhandler() call doesn't include the
21 * "events" field, thus the kqueue filter used is unknown.
22 *
23 * We have a seperate fixed array addqueue() for stuff we're adding to
24 * the queue; this gets flushed if it's full or held over until we
25 * next call handleevents().
26 */
27
28 #define UPDATEQUEUESIZE 100
29
30 struct kevent addqueue[UPDATEQUEUESIZE];
31 struct kevent *eventfds;
32
33 unsigned int maxfds;
34 unsigned int updates;
35
36 int kq;
37
38 int eventadds;
39 int eventdels;
40 int eventexes;
41
42 /* How many fds are currently registered */
43 int regfds;
44
45 void eventstats(int hooknum, void *arg);
46
47 void inithandlers() {
48 regfds=0;
49 updates=0;
50 eventadds=eventdels=eventexes=0;
51 maxfds=0;
52 eventfds=NULL;
53 kq=kqueue();
54 registerhook(HOOK_CORE_STATSREQUEST, &eventstats);
55 }
56
57 /*
58 * checkindex():
59 * Given the number of a new file descriptor, makes sure that the array
60 * will be big enough to deal with it.
61 */
62
63 void checkindex(unsigned index) {
64 int oldmax=maxfds;
65
66 if (index<maxfds) {
67 return;
68 }
69
70 while (maxfds<=index) {
71 maxfds+=GROWFDS;
72 }
73
74 eventfds=(struct kevent *)realloc((void *)eventfds,maxfds*sizeof(struct kevent));
75 memset(&eventfds[oldmax],0,(maxfds-oldmax)*sizeof(struct kevent));
76 }
77
78 /*
79 * registerhandler():
80 * Create a kevent structure and put it on the list to be added next
81 * time kevent() is called. If that list is full, we call kevent()
82 * to flush it first.
83 *
84 * We pass the handler in as the udata field to the kernel, this way
85 * we don't have to hunt for it when the kernel throws the kevent back
86 * at us.
87 */
88
89 int registerhandler(int fd, short events, FDHandler handler) {
90 checkindex(fd);
91
92 /* Check that it's not already registered */
93 if (eventfds[fd].filter!=0) {
94 return 1;
95 }
96
97 eventfds[fd].ident=fd;
98 if (events & POLLIN) {
99 eventfds[fd].filter=EVFILT_READ;
100 } else {
101 eventfds[fd].filter=EVFILT_WRITE;
102 }
103 eventfds[fd].flags=EV_ADD;
104 eventfds[fd].fflags=0;
105 eventfds[fd].data=0;
106 eventfds[fd].udata=(void *)handler;
107
108 /* Error("core",ERR_DEBUG,"Adding fd %d filter %d",fd,eventfds[fd].filter); */
109
110 if (updates>=UPDATEQUEUESIZE) {
111 kevent(kq, addqueue, updates, NULL, 0, NULL);
112 updates=0;
113 }
114
115 addqueue[updates++]=eventfds[fd];
116
117 eventadds++;
118 regfds++;
119 return 0;
120 }
121
122 /*
123 * deregisterhandler():
124 * Removes the fd's kevent from the kqueue. Note that if we're
125 * going to be closing the fd, it will automatically be removed
126 * from the queue so we don't have to do anything except the close().
127 */
128
129 int deregisterhandler(int fd, int doclose) {
130
131 if (!doclose) {
132 if (updates>=UPDATEQUEUESIZE) {
133 kevent(kq, addqueue, updates, NULL, 0, NULL);
134 updates=0;
135 }
136
137 eventfds[fd].flags=EV_DELETE;
138 addqueue[updates++]=eventfds[fd];
139
140 /* Error("core",ERR_DEBUG,"Deleting fd %d filter %d",fd,eventfds[fd].filter); */
141 } else {
142 close(fd);
143 }
144
145 regfds--;
146 eventdels++;
147 eventfds[fd].filter=0;
148
149 return 0;
150 }
151
152 /*
153 * handleevents():
154 * Call kevent() and handle and call appropiate handlers
155 * for any sockets that come up.
156 *
157 * This is O(n) in the number of fds returned, rather than the number
158 * of fds polled as in the poll() case.
159 */
160
161 int handleevents(int timeout) {
162 int i,res;
163 struct timespec ts;
164 struct kevent theevents[100];
165 short revents;
166
167 ts.tv_sec=(timeout/1000);
168 ts.tv_nsec=(timeout%1000)*1000000;
169
170 res=kevent(kq, addqueue, updates, theevents, 100, &ts);
171 updates=0;
172
173 if (res<0) {
174 return 1;
175 }
176
177 for (i=0;i<res;i++) {
178 revents=0;
179
180 /* If EV_ERROR is set it's a failed addition of a kevent to the queue.
181 * This shouldn't happen so we flag a warning. */
182 if(theevents[i].flags & EV_ERROR) {
183 Error("core",ERR_WARNING,"Got EV_ERROR return from kqueue: fd %d filter %d error %d",theevents[i].ident,theevents[i].filter,theevents[i].data);
184 } else {
185 /* Otherwise, translate the result into poll() format.. */
186 if(theevents[i].filter==EVFILT_READ) {
187 if (theevents[i].data>0) {
188 revents|=POLLIN;
189 }
190 } else {
191 if (theevents[i].data>0) {
192 revents|=POLLOUT;
193 }
194 }
195
196 if (theevents[i].flags & EV_EOF || theevents[i].data<0) {
197 revents|=POLLERR;
198 }
199
200 /* Call the handler */
201 ((FDHandler)(theevents[i].udata))(theevents[i].ident, revents);
202 eventexes++;
203 }
204 }
205 return 0;
206 }
207
208 void eventstats(int hooknum, void *arg) {
209 char buf[512];
210 long level=(long) arg;
211
212 if (level>5) {
213 sprintf(buf,"Events :%7d fds registered, %7d fds deregistered",eventadds,eventdels);
214 triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
215 sprintf(buf,"Events :%7d events triggered, %6d fds active",eventexes,regfds);
216 triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
217 }
218 }
219