[IOMultiplexer] Added asynchronous DNS Lookups
[IOMultiplexer.git] / src / IOEngine_select.c
1 /* IOEngine_select.c - IOMultiplexer
2  * Copyright (C) 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 <errno.h>
19 #include <time.h>
20 #ifdef WIN32
21 #define _WIN32_WINNT 0x501
22 #include <windows.h>
23 #include <winsock2.h>
24 #else
25 #include <string.h>
26 #include <stdio.h>
27 #endif
28
29 static int engine_select_timer_delay_fix;
30
31 static int engine_select_init() {
32     engine_select_timer_delay_fix = 0;
33     return 1;
34 }
35
36 static void engine_select_add(struct IOLowlevelDescriptor *iold) {
37     #ifdef WIN32
38     struct IODescriptor *iofd;
39     if((iofd = IOLOWLEVEL_GET_IOFD(iold))) {
40         if(iofd->type == IOTYPE_STDIN)
41             SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT);
42     }
43     #endif
44     /* empty */
45 }
46
47 static void engine_select_remove(struct IODescriptor *iofd) {
48     /* empty */
49 }
50
51 static void engine_select_update(struct IODescriptor *iofd) {
52     /* empty */
53 }
54
55 static void engine_select_loop(struct timeval *timeout) {
56     fd_set read_fds;
57     fd_set write_fds;
58     unsigned int fds_size = 0;
59     struct IOLowlevelDescriptor *iold, *tmp_iold;
60     struct IODescriptor *iofd;
61     struct timeval now, tdiff;
62     int select_result;
63     int timer_fix;
64     
65     gettimeofday(&now, NULL);
66     
67     //clear fds
68     FD_ZERO(&read_fds);
69     FD_ZERO(&write_fds);
70     
71     select_result = 0;
72     for(iold = first_descriptor; iold; iold = tmp_iold) {
73         tmp_iold = iold->next;
74         iofd = IOLOWLEVEL_GET_IOFD(iold);
75         if(iofd && iofd->type == IOTYPE_STDIN) {
76             #ifdef WIN32
77             //WIN32 doesn't support stdin within select
78             //just try to read the single events from the console
79             DWORD dwRead;
80             INPUT_RECORD inRecords[128];
81             unsigned int i;
82             int read_bytes = 0;
83             GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead);
84             if(dwRead)
85                 ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 128, &dwRead);
86             for (i = 0; i < dwRead; ++i) {
87                 if (inRecords[i].EventType == KEY_EVENT) {
88                     const char c = inRecords[i].Event.KeyEvent.uChar.AsciiChar;
89                     if (inRecords[i].Event.KeyEvent.bKeyDown && c != 0) {
90                         iofd->readbuf.buffer[iofd->readbuf.bufpos + read_bytes] = c;
91                         read_bytes++;
92                     }
93                 }
94             }
95             if(read_bytes)
96                 iohandler_events(iofd, read_bytes, 0);
97             if(read_bytes >= 128) {
98                 timeout->tv_sec = 0;
99                 timeout->tv_usec = 1;
100                 //minimal timeout
101             } else {
102                 timeout->tv_sec = 0;
103                 timeout->tv_usec = 100000;
104             }
105             #else
106             if(iold->fd > fds_size)
107                 fds_size = iold->fd;
108             FD_SET(iold->fd, &read_fds);
109             select_result++;
110             #endif
111         }
112         else if(iold->fd >= 0) {
113             if(iofd && iofd->state == IO_CLOSED) 
114                 continue;
115             if(iold->fd > fds_size)
116                 fds_size = iold->fd;
117             select_result++;
118             if(iold->flags & IOFLAGS_WANT_READ)
119                 FD_SET(iold->fd, &read_fds);
120             if(iold->flags & IOFLAGS_WANT_WRITE)
121                 FD_SET(iofd->fd, &write_fds);
122         }
123     }
124     
125     while(timer_priority) {
126         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
127         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
128         if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
129             iofd = IOLOWLEVEL_GET_IOFD(iold);
130             if(iofd && iofd->constant_timeout) {
131                 tdiff.tv_sec = 0;
132                 iohandler_set_timeout(iofd, &tdiff);
133                 if(iofd)
134                     iohandler_events(timer_priority, 0, 0);
135                 else
136                     timer_priority->data.callback(timer_priority, 0, 0);
137             } else {
138                 iohandler_events(timer_priority, 0, 0);
139                 iohandler_close(timer_priority); //also sets timer_priority to the next timed element
140             }
141             continue;
142         } else if(tdiff.tv_usec < 0) {
143             tdiff.tv_sec--;
144             tdiff.tv_usec += 1000000; //1 sec
145         }
146         if(timeval_is_smaler((&tdiff), timeout)) {
147             timeout->tv_sec = tdiff.tv_sec;
148             timeout->tv_usec = tdiff.tv_usec + engine_select_timer_delay_fix;
149             if(timeout->tv_usec < 0) {
150                 timeout->tv_sec--;
151                 timeout->tv_usec += 1000000;
152             }
153         }
154         break;
155     }
156     
157     if(select_result) {
158         //select system call
159         select_result = select(fds_size + 1, &read_fds, &write_fds, NULL, timeout);
160     } else {
161         #ifdef WIN32
162         Sleep((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000) + 1);
163         #else
164         struct timespec usleep_time;
165         usleep_time.tv_sec = timeout->tv_sec;
166         usleep_time.tv_nsec = timeout->tv_usec * 1000;
167         nanosleep(&usleep_time, NULL);
168         #endif
169     }
170     
171     if (select_result < 0) {
172         if (errno != EINTR) {
173             iohandler_log(IOLOG_FATAL, "select() failed with errno %d %d: %s", select_result, errno, strerror(errno));
174             return;
175         }
176     }
177     
178     gettimeofday(&now, NULL);
179     
180     //check all descriptors
181     for(iold = first_descriptor; iold; iold = tmp_iold) {
182         tmp_iold = iold->next;
183         iofd = IOLOWLEVEL_GET_IOFD(iold);
184         if((iofd && iofd->type == IOTYPE_STDIN) || iold->fd >= 0) {
185             if(FD_ISSET(iold->fd, &read_fds) || FD_ISSET(iold->fd, &write_fds)) {
186                 if(iofd)
187                     iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds));
188                 else
189                     iold->data.callback(iold, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds));
190                 continue;
191             }
192         }
193     }
194     
195     //check timers
196     while(timer_priority) {
197         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
198         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
199         timer_fix = (tdiff.tv_sec * 1000000) + tdiff.tv_usec;
200         if(timer_fix <= 100) {
201             if(timer_fix + 100 < engine_select_timer_delay_fix || timer_fix - 100 > engine_select_timer_delay_fix) {
202                 engine_select_timer_delay_fix = ((engine_select_timer_delay_fix * 19) + timer_fix) / 20;
203                 iohandler_log(IOLOG_DEBUG, "timer delay fix set to: %d us", engine_select_timer_delay_fix);
204             }
205             if(timer_priority->constant_timeout) {
206                 tdiff.tv_sec = 0;
207                 iohandler_set_timeout(timer_priority, &tdiff);
208                 iohandler_events(timer_priority, 0, 0);
209             } else {
210                 iohandler_events(timer_priority, 0, 0);
211                 iohandler_close(timer_priority); //also sets timer_priority to the next timed element
212             }
213             continue;
214         }
215         break;
216     }
217     
218 }
219
220 static void engine_select_cleanup() {
221     /* empty */
222 }
223
224 struct IOEngine engine_select = {
225     .name = "select",
226     .init = engine_select_init,
227     .add = engine_select_add,
228     .remove = engine_select_remove,
229     .update = engine_select_update,
230     .loop = engine_select_loop,
231     .cleanup = engine_select_cleanup,
232 };