]>
Commit | Line | Data |
---|---|---|
2c5db955 CP |
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]; | |
c3db6f7e | 210 | long level=(long) arg; |
2c5db955 CP |
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 |