]>
Commit | Line | Data |
---|---|---|
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 | void finihandlers() { | |
58 | deregisterhook(HOOK_CORE_STATSREQUEST, &eventstats); | |
59 | } | |
60 | ||
61 | ||
62 | /* | |
63 | * checkindex(): | |
64 | * Given the number of a new file descriptor, makes sure that the array | |
65 | * will be big enough to deal with it. | |
66 | */ | |
67 | ||
68 | void checkindex(unsigned index) { | |
69 | int oldmax=maxfds; | |
70 | ||
71 | if (index<maxfds) { | |
72 | return; | |
73 | } | |
74 | ||
75 | while (maxfds<=index) { | |
76 | maxfds+=GROWFDS; | |
77 | } | |
78 | ||
79 | eventfds=(struct kevent *)realloc((void *)eventfds,maxfds*sizeof(struct kevent)); | |
80 | memset(&eventfds[oldmax],0,(maxfds-oldmax)*sizeof(struct kevent)); | |
81 | } | |
82 | ||
83 | /* | |
84 | * registerhandler(): | |
85 | * Create a kevent structure and put it on the list to be added next | |
86 | * time kevent() is called. If that list is full, we call kevent() | |
87 | * to flush it first. | |
88 | * | |
89 | * We pass the handler in as the udata field to the kernel, this way | |
90 | * we don't have to hunt for it when the kernel throws the kevent back | |
91 | * at us. | |
92 | */ | |
93 | ||
94 | int registerhandler(int fd, short events, FDHandler handler) { | |
95 | checkindex(fd); | |
96 | ||
97 | /* Check that it's not already registered */ | |
98 | if (eventfds[fd].filter!=0) { | |
99 | return 1; | |
100 | } | |
101 | ||
102 | eventfds[fd].ident=fd; | |
103 | if (events & POLLIN) { | |
104 | eventfds[fd].filter=EVFILT_READ; | |
105 | } else { | |
106 | eventfds[fd].filter=EVFILT_WRITE; | |
107 | } | |
108 | eventfds[fd].flags=EV_ADD; | |
109 | eventfds[fd].fflags=0; | |
110 | eventfds[fd].data=0; | |
111 | eventfds[fd].udata=(void *)handler; | |
112 | ||
113 | /* Error("core",ERR_DEBUG,"Adding fd %d filter %d",fd,eventfds[fd].filter); */ | |
114 | ||
115 | if (updates>=UPDATEQUEUESIZE) { | |
116 | kevent(kq, addqueue, updates, NULL, 0, NULL); | |
117 | updates=0; | |
118 | } | |
119 | ||
120 | addqueue[updates++]=eventfds[fd]; | |
121 | ||
122 | eventadds++; | |
123 | regfds++; | |
124 | return 0; | |
125 | } | |
126 | ||
127 | /* | |
128 | * deregisterhandler(): | |
129 | * Removes the fd's kevent from the kqueue. Note that if we're | |
130 | * going to be closing the fd, it will automatically be removed | |
131 | * from the queue so we don't have to do anything except the close(). | |
132 | */ | |
133 | ||
134 | int deregisterhandler(int fd, int doclose) { | |
135 | ||
136 | if (!doclose) { | |
137 | if (updates>=UPDATEQUEUESIZE) { | |
138 | kevent(kq, addqueue, updates, NULL, 0, NULL); | |
139 | updates=0; | |
140 | } | |
141 | ||
142 | eventfds[fd].flags=EV_DELETE; | |
143 | addqueue[updates++]=eventfds[fd]; | |
144 | ||
145 | /* Error("core",ERR_DEBUG,"Deleting fd %d filter %d",fd,eventfds[fd].filter); */ | |
146 | } else { | |
147 | close(fd); | |
148 | } | |
149 | ||
150 | regfds--; | |
151 | eventdels++; | |
152 | eventfds[fd].filter=0; | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | /* | |
158 | * handleevents(): | |
159 | * Call kevent() and handle and call appropiate handlers | |
160 | * for any sockets that come up. | |
161 | * | |
162 | * This is O(n) in the number of fds returned, rather than the number | |
163 | * of fds polled as in the poll() case. | |
164 | */ | |
165 | ||
166 | int handleevents(int timeout) { | |
167 | int i,res; | |
168 | struct timespec ts; | |
169 | struct kevent theevents[100]; | |
170 | short revents; | |
171 | ||
172 | ts.tv_sec=(timeout/1000); | |
173 | ts.tv_nsec=(timeout%1000)*1000000; | |
174 | ||
175 | res=kevent(kq, addqueue, updates, theevents, 100, &ts); | |
176 | updates=0; | |
177 | ||
178 | if (res<0) { | |
179 | return 1; | |
180 | } | |
181 | ||
182 | for (i=0;i<res;i++) { | |
183 | revents=0; | |
184 | ||
185 | /* If EV_ERROR is set it's a failed addition of a kevent to the queue. | |
186 | * This shouldn't happen so we flag a warning. */ | |
187 | if(theevents[i].flags & EV_ERROR) { | |
188 | 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); | |
189 | } else { | |
190 | /* Otherwise, translate the result into poll() format.. */ | |
191 | if(theevents[i].filter==EVFILT_READ) { | |
192 | if (theevents[i].data>0) { | |
193 | revents|=POLLIN; | |
194 | } | |
195 | } else { | |
196 | if (theevents[i].data>0) { | |
197 | revents|=POLLOUT; | |
198 | } | |
199 | } | |
200 | ||
201 | if (theevents[i].flags & EV_EOF || theevents[i].data<0) { | |
202 | revents|=POLLERR; | |
203 | } | |
204 | ||
205 | /* Call the handler */ | |
206 | ((FDHandler)(theevents[i].udata))(theevents[i].ident, revents); | |
207 | eventexes++; | |
208 | } | |
209 | } | |
210 | return 0; | |
211 | } | |
212 | ||
213 | void eventstats(int hooknum, void *arg) { | |
214 | char buf[512]; | |
215 | long level=(long) arg; | |
216 | ||
217 | if (level>5) { | |
218 | sprintf(buf,"Events :%7d fds registered, %7d fds deregistered",eventadds,eventdels); | |
219 | triggerhook(HOOK_CORE_STATSREPLY,(void *)buf); | |
220 | sprintf(buf,"Events :%7d events triggered, %6d fds active",eventexes,regfds); | |
221 | triggerhook(HOOK_CORE_STATSREPLY,(void *)buf); | |
222 | } | |
223 | } | |
224 |