[IOMultiplexer] do not request events from closed descriptors
[NeonServV5.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 #ifdef WIN32
20 #define _WIN32_WINNT 0x501
21 #include <windows.h>
22 #include <winsock2.h>
23 #else
24 #include <string.h>
25 #include <stdio.h>
26 #endif
27
28 static int engine_select_init() {
29     /* empty */
30     return 1;
31 }
32
33 static void engine_select_add(struct IODescriptor *iofd) {
34     #ifdef WIN32
35     if(iofd->type == IOTYPE_STDIN)
36         SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT);
37     #endif
38     /* empty */
39 }
40
41 static void engine_select_remove(struct IODescriptor *iofd) {
42     /* empty */
43 }
44
45 static void engine_select_update(struct IODescriptor *iofd) {
46     /* empty */
47 }
48
49 static void engine_select_loop(struct timeval *timeout) {
50     fd_set read_fds;
51     fd_set write_fds;
52     unsigned int fds_size = 0;
53     struct IODescriptor *iofd, *tmp_iofd;
54     struct timeval now, tdiff;
55     int select_result;
56     
57     gettimeofday(&now, NULL);
58     
59     //clear fds
60     FD_ZERO(&read_fds);
61     FD_ZERO(&write_fds);
62     
63     for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
64         tmp_iofd = iofd->next;
65         if(iofd->type == IOTYPE_STDIN) {
66             #ifdef WIN32
67             //WIN32 doesn't support stdin within select
68             //just try to read the single events from the console
69             DWORD dwRead;
70             INPUT_RECORD inRecords[128];
71             unsigned int i;
72             int read_bytes = 0;
73             GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead);
74             if(dwRead)
75                 ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 128, &dwRead);
76             for (i = 0; i < dwRead; ++i) {
77                 if (inRecords[i].EventType == KEY_EVENT) {
78                     const char c = inRecords[i].Event.KeyEvent.uChar.AsciiChar;
79                     if (inRecords[i].Event.KeyEvent.bKeyDown && c != 0) {
80                         iofd->readbuf.buffer[iofd->readbuf.bufpos + read_bytes] = c;
81                         read_bytes++;
82                     }
83                 }
84             }
85             if(read_bytes)
86                 iohandler_events(iofd, read_bytes, 0);
87             if(read_bytes >= 128) {
88                 timeout->tv_sec = 0;
89                 timeout->tv_usec = 1;
90                 //minimal timeout
91             } else {
92                 timeout->tv_sec = 0;
93                 timeout->tv_usec = 100000;
94             }
95             #else
96             if(iofd->fd > fds_size)
97                 fds_size = iofd->fd;
98             FD_SET(iofd->fd, &read_fds);
99             #endif
100         }
101         else if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT) {
102             if(iofd->state == IO_CLOSED) 
103                 continue;
104             if(iofd->fd > fds_size)
105                 fds_size = iofd->fd;
106             FD_SET(iofd->fd, &read_fds);
107             if(iohandler_wants_writes(iofd))
108                 FD_SET(iofd->fd, &write_fds);
109         }
110     }
111     
112     while(timer_priority) {
113         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
114         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
115         if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
116             iohandler_events(timer_priority, 0, 0);
117             iohandler_close(timer_priority); //also sets timer_priority to the next timed element
118             continue;
119         } else if(tdiff.tv_usec < 0) {
120             tdiff.tv_sec--;
121             tdiff.tv_usec += 1000000; //1 sec
122         }
123         if(timeval_is_smaler((&tdiff), timeout)) {
124             timeout->tv_sec = tdiff.tv_sec;
125             timeout->tv_usec = tdiff.tv_usec;
126         }
127         break;
128     }
129     
130     //select system call
131     select_result = select(fds_size + 1, &read_fds, &write_fds, NULL, timeout);
132     
133     if (select_result < 0) {
134         if (errno != EINTR) {
135             iohandler_log(IOLOG_FATAL, "select() failed with errno %d %d: %s", select_result, errno, strerror(errno));
136             return;
137         }
138     }
139     
140     gettimeofday(&now, NULL);
141     
142     //check all descriptors
143     for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
144         tmp_iofd = iofd->next;
145         if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN) {
146             if(FD_ISSET(iofd->fd, &read_fds) || FD_ISSET(iofd->fd, &write_fds)) {
147                 iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds));
148                 continue;
149             }
150         }
151     }
152     
153     //check timers
154     while(timer_priority) {
155         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
156         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
157         if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
158             iohandler_events(timer_priority, 0, 0);
159             iohandler_close(timer_priority); //also sets timer_priority to the next timed element
160             continue;
161         }
162         break;
163     }
164     
165 }
166
167 static void engine_select_cleanup() {
168     /* empty */
169 }
170
171 struct IOEngine engine_select = {
172     .name = "select",
173     .init = engine_select_init,
174     .add = engine_select_add,
175     .remove = engine_select_remove,
176     .update = engine_select_update,
177     .loop = engine_select_loop,
178     .cleanup = engine_select_cleanup,
179 };