From 8ba4de3b3b5be1d229df13678b912f6ce3a6cf07 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sun, 12 Aug 2012 03:10:20 +0200 Subject: [PATCH] [IOMultiplexer] added win32 io-engine (WSAAsyncSelect) --- src/IOEngine_select.c | 2 +- src/IOEngine_win32.c | 275 ++++++++++++++++++++++++++++++++++++++++++ src/IOHandler.c | 11 +- src/Makefile | 2 +- 4 files changed, 280 insertions(+), 10 deletions(-) create mode 100644 src/IOEngine_win32.c diff --git a/src/IOEngine_select.c b/src/IOEngine_select.c index 74e8431..8c25ba6 100644 --- a/src/IOEngine_select.c +++ b/src/IOEngine_select.c @@ -15,12 +15,12 @@ * along with this program. If not, see . */ #include "IOEngine.h" +#include #ifdef WIN32 #define _WIN32_WINNT 0x501 #include #include #else -#include #include #include #endif diff --git a/src/IOEngine_win32.c b/src/IOEngine_win32.c new file mode 100644 index 0000000..bce6fc1 --- /dev/null +++ b/src/IOEngine_win32.c @@ -0,0 +1,275 @@ +/* IOEngine_win32.c - IOMultiplexer + * Copyright (C) 2012 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "IOEngine.h" + +#ifdef WIN32 + +#define _WIN32_WINNT 0x501 +#include +#include + +/* This is massively kludgy. Unfortunately, the only performant I/O + * multiplexer with halfway decent semantics under Windows is + * WSAAsyncSelect() -- which requires a window that can receive + * messages. + * + * So ioset_win32_init() creates a hidden window and sets it up for + * asynchronous socket notifications. + */ + +#define IDT_TIMER1 1000 +#define IDT_TIMER2 1001 +#define IDT_SOCKET 1002 + +static HWND ioset_window; + +static struct IODescriptor *engine_win32_get_iofd(int fd) { + struct IODescriptor *iofd; + for(iofd = first_descriptor; iofd; iofd = iofd->next) { + if(iofd->fd == fd) + return iofd; + } + return NULL; +} + +static LRESULT CALLBACK engine_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + struct IODescriptor *iofd; + int events; + struct timeval now, tdiff; + + gettimeofday(&now, NULL); + + if (hWnd == ioset_window) switch (uMsg) + { + case IDT_TIMER1: + return 0; + case IDT_TIMER2: + //User Timer + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; + } + break; + } + return 0; + case IDT_SOCKET: + iofd = engine_win32_get_iofd(wParam); + events = WSAGETSELECTEVENT(lParam); + + iohandler_events(iofd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0); + return 0; + case WM_QUIT: + return 0; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +static int engine_win32_init() { + WNDCLASSEX wcx; + HINSTANCE hinst; + WSADATA wsadata; + + // Start Windows Sockets. + if (WSAStartup(MAKEWORD(2, 0), &wsadata)) { + iohandler_log(IOLOG_FATAL, "Unable to start Windows Sockets"); + return 0; + } + + // Get Windows HINSTANCE. + hinst = GetModuleHandle(NULL); + + // Describe and register a window class. + memset(&wcx, 0, sizeof(wcx)); + wcx.cbSize = sizeof(wcx); + wcx.lpfnWndProc = engine_win32_wndproc; + wcx.hInstance = hinst; + wcx.lpszClassName = "IOMultiplexerMainWindow"; + if (!RegisterClassEx(&wcx)) + return 0; + + ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL); + if (!ioset_window) + return 0; + return 1; +} + +static long engine_win32_events(struct IODescriptor *iofd) { + switch (iofd->state) { + case IO_CLOSED: + return 0; + case IO_LISTENING: + return FD_ACCEPT; + case IO_CONNECTING: + return FD_CONNECT; + case IO_CONNECTED: + case IO_SSLWAIT: + return FD_READ | FD_CLOSE | (iohandler_wants_writes(iofd) ? FD_WRITE : 0); + } + return 0; +} + +static void engine_win32_update(struct IODescriptor *iofd) { + long events; + + if(iofd->type == IOTYPE_STDIN) + return; + + events = engine_win32_events(iofd); + WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, events); +} + +static void engine_win32_add(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_STDIN) + return; + + engine_win32_update(iofd); +} + +static void engine_win32_remove(struct IODescriptor *iofd) { + unsigned long ulong; + + if(iofd->type == IOTYPE_STDIN) + return; + + WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, 0); + + ulong = 0; + ioctlsocket(iofd->fd, FIONBIO, &ulong); +} + +static void engine_win32_loop(struct timeval *timeout) { + MSG msg; + BOOL not_really_bool; + int msec, cmsec, sett2; + struct timeval now, tdiff; + struct IODescriptor *iofd, *tmp_iofd; + + gettimeofday(&now, NULL); + + for(iofd = first_descriptor; iofd; iofd = tmp_iofd) { + tmp_iofd = iofd->next; + if(iofd->type == IOTYPE_STDIN) { + #ifdef WIN32 + //WIN32 doesn't support stdin within select + //just try to read the single events from the console + DWORD dwRead; + INPUT_RECORD inRecords[128]; + unsigned int i; + int read_bytes = 0; + GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead); + if(dwRead) + ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 128, &dwRead); + for (i = 0; i < dwRead; ++i) { + if (inRecords[i].EventType == KEY_EVENT) { + const char c = inRecords[i].Event.KeyEvent.uChar.AsciiChar; + if (inRecords[i].Event.KeyEvent.bKeyDown && c != 0) { + iofd->readbuf.buffer[iofd->readbuf.bufpos + read_bytes] = c; + read_bytes++; + } + } + } + if(read_bytes) + iohandler_events(iofd, read_bytes, 0); + if(read_bytes >= 128) { + timeout->tv_sec = 0; + timeout->tv_usec = 1; + //minimal timeout + } else { + timeout->tv_sec = 0; + timeout->tv_usec = 100000; + } + #else + if(iofd->fd > fds_size) + fds_size = iofd->fd; + FD_SET(iofd->fd, &read_fds); + #endif + } + } + + // Make sure we are woken up after the appropriate time. + msec = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000); + SetTimer(ioset_window, IDT_TIMER1, msec, NULL); + + //set additional User Timer (if ther's one) + sett2 = 0; + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec < 1000)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; + } else if(tdiff.tv_usec < 0) { + tdiff.tv_sec--; + tdiff.tv_usec += 1000000; //1 sec + } + cmsec = (tdiff.tv_sec * 1000) + (tdiff.tv_usec / 1000); + if(cmsec < msec) { + sett2 = 1; + msec = cmsec; + } + break; + } + if(sett2) + SetTimer(ioset_window, IDT_TIMER2, msec, NULL); + + // Do a blocking read of the message queue. + not_really_bool = GetMessage(&msg, NULL, 0, 0); + KillTimer(ioset_window, IDT_TIMER1); + if(sett2) + KillTimer(ioset_window, IDT_TIMER2); + if (not_really_bool <=0) + return; + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +static void engine_win32_cleanup() { + DestroyWindow(ioset_window); + ioset_window = NULL; + WSACleanup(); +} + +struct IOEngine engine_win32 = { + .name = "win32", + .init = engine_win32_init, + .add = engine_win32_add, + .remove = engine_win32_remove, + .update = engine_win32_update, + .loop = engine_win32_loop, + .cleanup = engine_win32_cleanup, +}; + +#else + +struct IOEngine engine_win32 = { + .name = "win32", + .init = NULL, + .add = NULL, + .remove = NULL, + .update = NULL, + .loop = NULL, + .cleanup = NULL, +}; + +#endif diff --git a/src/IOHandler.c b/src/IOHandler.c index 3c5bc3d..52398de 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -61,6 +61,7 @@ void iohandler_log(enum IOLogType type, char *text, ...) { extern struct IOEngine engine_select; /* select system call (should always be useable) */ extern struct IOEngine engine_kevent; extern struct IOEngine engine_epoll; +extern struct IOEngine engine_win32; struct IOEngine *engine = NULL; @@ -71,6 +72,8 @@ static void iohandler_init_engine() { engine = &engine_kevent; if(!engine && engine_epoll.init && engine_epoll.init()) engine = &engine_epoll; + if(!engine && engine_win32.init && engine_win32.init()) + engine = &engine_win32; if (!engine) { if(engine_select.init()) @@ -82,14 +85,6 @@ static void iohandler_init_engine() { } iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name); iohandler_ssl_init(); - #ifdef WIN32 - WSADATA wsadata; - // Start Windows Sockets. - if (WSAStartup(MAKEWORD(2, 0), &wsadata)) { - iohandler_log(IOLOG_FATAL, "Unable to start Windows Sockets"); - return; - } - #endif } static void iohandler_append(struct IODescriptor *descriptor) { diff --git a/src/Makefile b/src/Makefile index 188780c..434a929 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ CC = gcc CFLAGS = -g -O0 -Wall -Wshadow -Werror LDFLAGS = -lws2_32 -OBJ = IOEngine_epoll.o IOEngine_kevent.o IOEngine_select.o IOHandler.o IOHandler_SSL.o main.o +OBJ = IOEngine_epoll.o IOEngine_kevent.o IOEngine_select.o IOEngine_win32.o IOHandler.o IOHandler_SSL.o main.o all: $(OBJ) $(CC) $(CFLAGS) -oiotest $(OBJ) $(LDFLAGS) -- 2.20.1