[IOMultiplexer] added win32 io-engine (WSAAsyncSelect)
[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->fd > fds_size)
103                 fds_size = iofd->fd;
104             FD_SET(iofd->fd, &read_fds);
105             if(iohandler_wants_writes(iofd))
106                 FD_SET(iofd->fd, &write_fds);
107         }
108     }
109     
110     while(timer_priority) {
111         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
112         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
113         if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
114             iohandler_events(timer_priority, 0, 0);
115             iohandler_close(timer_priority); //also sets timer_priority to the next timed element
116             continue;
117         } else if(tdiff.tv_usec < 0) {
118             tdiff.tv_sec--;
119             tdiff.tv_usec += 1000000; //1 sec
120         }
121         if(timeval_is_smaler((&tdiff), timeout)) {
122             timeout->tv_sec = tdiff.tv_sec;
123             timeout->tv_usec = tdiff.tv_usec;
124         }
125         break;
126     }
127     
128     //select system call
129     select_result = select(fds_size + 1, &read_fds, &write_fds, NULL, timeout);
130     
131     if (select_result < 0) {
132         if (errno != EINTR) {
133             iohandler_log(IOLOG_FATAL, "select() failed with errno %d %d: %s", select_result, errno, strerror(errno));
134             return;
135         }
136     }
137     
138     gettimeofday(&now, NULL);
139     
140     //check all descriptors
141     for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
142         tmp_iofd = iofd->next;
143         if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN) {
144             if(FD_ISSET(iofd->fd, &read_fds) || FD_ISSET(iofd->fd, &write_fds)) {
145                 iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds));
146                 continue;
147             }
148         }
149     }
150     
151     //check timers
152     while(timer_priority) {
153         tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
154         tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
155         if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
156             iohandler_events(timer_priority, 0, 0);
157             iohandler_close(timer_priority); //also sets timer_priority to the next timed element
158             continue;
159         }
160         break;
161     }
162     
163 }
164
165 static void engine_select_cleanup() {
166     /* empty */
167 }
168
169 struct IOEngine engine_select = {
170     .name = "select",
171     .init = engine_select_init,
172     .add = engine_select_add,
173     .remove = engine_select_remove,
174     .update = engine_select_update,
175     .loop = engine_select_loop,
176     .cleanup = engine_select_cleanup,
177 };