]> jfr.im git - irc/quakenet/newserv.git/blob - core/events-epoll.c
3cb3a7a19ae4f908a6bbbac2af79d4ca7f1e788d
[irc/quakenet/newserv.git] / core / events-epoll.c
1 /*
2 * events.c: the event handling core, epoll() version
3 */
4
5 #include <stdio.h>
6 #include <sys/poll.h>
7 #include <sys/epoll.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <errno.h>
12
13 #include "../lib/array.h"
14 #include "events.h"
15 #include "error.h"
16 #include "hooks.h"
17
18 /* We need to track the handler for each fd - epoll() can
19 * return an fd or a pointer but not both :(. We'll keep the
20 * fd in the epoll_data structure for simplicity. */
21
22 typedef struct {
23 FDHandler handler;
24 } reghandler;
25
26 reghandler *eventhandlers;
27
28 unsigned int maxfds;
29
30 int eventadds;
31 int eventdels;
32 int eventexes;
33
34 /* How many fds are currently registered */
35 int regfds;
36 int epollfd;
37
38 void eventstats(int hooknum, void *arg);
39
40 void inithandlers() {
41 regfds=0;
42 eventadds=eventdels=eventexes=0;
43 maxfds=STARTFDS;
44 eventhandlers=(reghandler *)malloc(maxfds*sizeof(reghandler));
45 memset(eventhandlers,0,maxfds*sizeof(reghandler));
46
47 /* Get an epoll FD */
48 if ((epollfd=epoll_create(STARTFDS))<0) {
49 Error("events",ERR_STOP,"Unable to initialise epoll.");
50 }
51
52 registerhook(HOOK_CORE_STATSREQUEST, &eventstats);
53 }
54
55 /*
56 * checkindex():
57 * Given the number of a new file descriptor, makes sure that the array
58 * will be big enough to deal with it.
59 */
60
61 void checkindex(unsigned index) {
62 int oldmax=maxfds;
63
64 if (index<maxfds) {
65 return;
66 }
67
68 while (maxfds<=index) {
69 maxfds+=GROWFDS;
70 }
71
72 eventhandlers=(reghandler *)realloc((void *)eventhandlers,maxfds*sizeof(reghandler));
73 memset(&eventhandlers[oldmax],0,(maxfds-oldmax)*sizeof(reghandler));
74 }
75
76 /*
77 * polltoepoll():
78 * Converts a poll-style event variable to an epoll one.
79 */
80 unsigned int polltoepoll(short events) {
81 unsigned int epe=EPOLLERR|EPOLLHUP; /* Default event mask */
82
83 if (events & POLLIN) epe |= EPOLLIN;
84 if (events & POLLOUT) epe |= EPOLLOUT;
85 if (events & POLLPRI) epe |= EPOLLPRI;
86
87 return epe;
88 }
89
90 short epolltopoll(unsigned int events) {
91 short e=0;
92
93 if (events & EPOLLIN) e |= POLLIN;
94 if (events & EPOLLOUT) e |= POLLOUT;
95 if (events & EPOLLERR) e |= POLLERR;
96 if (events & EPOLLPRI) e |= POLLPRI;
97 if (events & EPOLLHUP) e |= POLLHUP;
98
99 return e;
100 }
101
102 /*
103 * registerhandler():
104 * Add an fd to the epoll array.
105 */
106
107 int registerhandler(int fd, short events, FDHandler handler) {
108 struct epoll_event epe;
109
110 checkindex(fd);
111
112 /* Check that it's not already registered */
113 if (eventhandlers[fd].handler!=NULL) {
114 Error("events",ERR_WARNING,"Attempting to register already-registered fd %d.",fd);
115 return 1;
116 }
117
118 eventhandlers[fd].handler=handler;
119
120 epe.data.fd=fd;
121 epe.events=polltoepoll(events);
122
123 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &epe)) {
124 Error("events",ERR_WARNING,"Error %d adding fd %d to epoll (events=%d)",errno,fd,epe.events);
125 return 1;
126 }
127
128 eventadds++;
129 regfds++;
130 return 0;
131 }
132
133 /*
134 * deregisterhandler():
135 * Remove an fd from the poll() array.
136 *
137 * The poll() version can't save any time if doclose is set, so
138 * we just do the same work and then close the socket if it's set.
139 *
140 * Now O(1)
141 */
142
143
144 int deregisterhandler(int fd, int doclose) {
145 struct epoll_event epe;
146
147 checkindex(fd);
148
149 /* Check that the handler exists */
150 if (eventhandlers[fd].handler==NULL) {
151 Error("events",ERR_WARNING,"Attempt to deregister unregistered fd: %d",fd);
152 return 1;
153 }
154
155 eventhandlers[fd].handler=NULL;
156
157 if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &epe)) {
158 Error("events",ERR_WARNING,"Error deleting FD %d from epoll: %d",fd,errno);
159 }
160
161 if (doclose) {
162 close(fd);
163 }
164
165 eventdels++;
166 regfds--;
167
168 return 0;
169 }
170
171 /*
172 * handleevents():
173 * Call epoll_wait() and handle and call appropiate handlers
174 * for any sockets that come up.
175 */
176
177 int handleevents(int timeout) {
178 int i,res;
179 struct epoll_event epes[100];
180
181 res=epoll_wait(epollfd, epes, 100, timeout);
182
183 if (res<0) {
184 Error("events",ERR_WARNING,"Error in epoll_wait(): %d",errno);
185 return 1;
186 }
187
188 for (i=0;i<res;i++) {
189 (eventhandlers[epes[i].data.fd].handler)(epes[i].data.fd, epolltopoll(epes[i].events));
190 eventexes++;
191 }
192
193 return 0;
194 }
195
196 void eventstats(int hooknum, void *arg) {
197 char buf[512];
198 long level=(long) arg;
199
200 if (level>5) {
201 sprintf(buf,"Events :%7d fds registered, %7d fds deregistered",eventadds,eventdels);
202 triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
203 sprintf(buf,"Events :%7d events triggered, %6d fds active",eventexes,regfds);
204 triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
205 }
206 }