modified IOMultiplexer (added epoll & kevent support)
[TransparentIRC.git] / src / IOEngine_epoll.c
1 /* IOEngine_epoll.c - TransparentIRC 0.1
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "IOEngine.h"
18 #include "IOHandler.h"
19
20 #ifdef HAVE_SYS_EPOLL_H
21 #include <sys/epoll.h>
22
23 #define MAX_EVENTS 32
24
25 static int epoll_fd;
26
27 static int engine_epoll_init() {
28     epoll_fd = epoll_create(1024);
29     if (epoll_fd < 0)
30         return 0;
31     return 1;
32 }
33
34 static void engine_epoll_add(struct IODescriptor *iofd) {
35     if(iofd->type == IOTYPE_TIMER) return;
36     //add descriptor to the epoll queue
37     struct epoll_event evt;
38     int res;
39
40     evt.events = EPOLLHUP | EPOLLIN | (iohandler_wants_writes(iofd) ? EPOLLOUT : 0);
41     evt.data.ptr = iofd;
42     res = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, iofd->fd, &evt);
43     if(res < 0) {
44         iohandler_log(IOLOG_ERROR, "could not add IODescriptor %d to epoll queue. (returned: %d)", iofd->fd, res);
45     }
46 }
47
48 static void engine_epoll_remove(struct IODescriptor *iofd) {
49     if(iofd->type == IOTYPE_TIMER) return;
50     struct epoll_event evt;
51     epoll_ctl(epoll_fd, EPOLL_CTL_DEL, iofd->fd, &evt);
52 }
53
54 static void engine_epoll_update(struct IODescriptor *iofd) {
55     if(iofd->type == IOTYPE_TIMER) return;
56     struct epoll_event evt;
57     int res;
58
59     evt.events = EPOLLHUP | EPOLLIN | (iohandler_wants_writes(iofd) ? EPOLLOUT : 0);
60     evt.data.ptr = iofd;
61     res = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, iofd->fd, &evt);
62     if(res < 0) {
63         iohandler_log(IOLOG_ERROR, "could not update IODescriptor %d in epoll queue. (returned: %d)", iofd->fd, res);
64     }
65 }
66
67 static void engine_epoll_loop(struct timeval *timeout) {
68     struct epoll_event evts[MAX_EVENTS];
69     struct timeval now, tdiff;
70     int msec;
71     int events;
72     int epoll_result;
73     
74     gettimeofday(&now, NULL);
75     
76     while(timer_priority) {
77         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
78         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
79         if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
80             iohandler_events(timer_priority, 0, 0);
81             iohandler_close(timer_priority); //also sets timer_priority to the next timed element
82             continue;
83         } else if(tdiff.tv_usec < 0) {
84             tdiff.tv_sec--;
85             tdiff.tv_usec += 1000000; //1 sec
86         }
87         if(timeval_is_smaler((&tdiff), timeout)) {
88             timeout->tv_sec = tdiff.tv_sec;
89             timeout->tv_usec = tdiff.tv_usec;
90         }
91         break;
92     }
93     
94     msec = timeout ? ((timeout->tv_sec * 1000 + timeout->tv_usec / 1000) + (timeout->tv_usec % 1000 != 0 ? 1 : 0)) : -1;
95     
96     //select system call
97     epoll_result = epoll_wait(epoll_fd, evts, MAX_EVENTS, msec);
98     
99     if (epoll_result < 0) {
100         if (errno != EINTR) {
101             iohandler_log(IOLOG_FATAL, "epoll_wait() failed with errno %d: %s", errno, strerror(errno));
102             return;
103         }
104     } else {
105         int i;
106         for(i = 0; i < epoll_result; i++) {
107             events = evts[i].events;
108             iohandler_events(evts[i].data.ptr, (events & (EPOLLIN | EPOLLHUP)), (events & EPOLLOUT));
109         }
110     }
111     
112     //check timers
113     while(timer_priority) {
114         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
115         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
116         if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
117             iohandler_events(timer_priority, 0, 0);
118             iohandler_close(timer_priority); //also sets timer_priority to the next timed element
119             continue;
120         }
121         break;
122     }
123     
124 }
125
126 static void engine_epoll_cleanup() {
127     close(epoll_fd);
128 }
129
130 struct IOEngine engine_epoll = {
131     .name = "epoll",
132     .init = engine_epoll_init,
133     .add = engine_epoll_add,
134     .remove = engine_epoll_remove,
135     .update = engine_epoll_update,
136     .loop = engine_epoll_loop,
137     .cleanup = engine_epoll_cleanup,
138 };
139
140 #else
141
142 struct IOEngine engine_epoll = {
143     .name = "epoll",
144     .init = NULL,
145     .add = NULL,
146     .remove = NULL,
147     .update = NULL,
148     .loop = NULL,
149     .cleanup = NULL,
150 };
151
152 #endif