From 42c04eb1d1732a3150fdbe02c5082b13f3230310 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Tue, 22 Apr 2014 17:21:40 +0200 Subject: [PATCH] sockfilt.c: clean up threaded approach and add documentation --- tests/server/sockfilt.c | 185 +++++++++++++++++++++++++++++----------- 1 file changed, 137 insertions(+), 48 deletions(-) diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index c4073ad7d..84b1be469 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -506,102 +506,170 @@ static void lograw(unsigned char *buffer, ssize_t len) * to re-create a select() function with support for other handle types. * * select() function with support for WINSOCK2 sockets and all - * other handle types supported by WaitForMultipleObjectsEx(). - * - * TODO: Differentiate between read/write/except for non-SOCKET handles. + * other handle types supported by WaitForMultipleObjectsEx() as + * well as disk files, anonymous and names pipes, and character input. * * http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx * http://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx */ -static DWORD WINAPI select_ws_stdin_wait_thread(LPVOID lpParameter) +struct select_ws_wait_data { + HANDLE handle; /* actual handle to wait for during select */ + HANDLE event; /* internal event to abort waiting thread */ +}; +static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter) { + struct select_ws_wait_data *data; HANDLE handle, handles[2]; INPUT_RECORD inputrecord; LARGE_INTEGER size, pos; DWORD type, length; - handle = GetStdHandle(STD_INPUT_HANDLE); - handles[0] = (HANDLE) lpParameter; - handles[1] = handle; - type = GetFileType(handle); + /* retrieve handles from internal structure */ + data = (struct select_ws_wait_data *) lpParameter; + if(data) { + handle = data->handle; + handles[0] = data->event; + handles[1] = handle; + free(data); + } + else + return -1; + /* retrieve the type of file to wait on */ + type = GetFileType(handle); switch(type) { case FILE_TYPE_DISK: + /* The handle represents a file on disk, this means: + * - WaitForMultipleObjectsEx will always be signalled for it. + * - comparison of current position in file and total size of + * the file can be used to check if we reached the end yet. + * + * Approach: Loop till either the internal event is signalled + * or if the end of the file has already been reached. + */ while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) == WAIT_OBJECT_0 + 1) { + /* get total size of file */ size.QuadPart = 0; if(GetFileSizeEx(handle, &size)) { + /* get the current position within the file */ pos.QuadPart = 0; if(SetFilePointerEx(handle, pos, &pos, FILE_CURRENT)) { - if(size.QuadPart == pos.QuadPart) + /* compare position with size, abort if not equal */ + if(size.QuadPart == pos.QuadPart) { + /* sleep and continue waiting */ SleepEx(100, FALSE); - else - break; + continue; + } } - else - break; } - else - break; + /* there is some data available, stop waiting */ + break; } break; case FILE_TYPE_CHAR: + /* The handle represents a character input, this means: + * - WaitForMultipleObjectsEx will be signalled on any kind of input, + * including mouse and window size events we do not care about. + * + * Approach: Loop till either the internal event is signalled + * or we get signalled for an actual key-event. + */ while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) == WAIT_OBJECT_0 + 1) { + /* check if this is an actual console handle */ if(GetConsoleMode(handle, &length)) { + /* retrieve an event from the console buffer */ length = 0; if(PeekConsoleInput(handle, &inputrecord, 1, &length)) { - if(length == 1 && inputrecord.EventType != KEY_EVENT) + /* check if the event is not an actual key-event */ + if(length == 1 && inputrecord.EventType != KEY_EVENT) { + /* purge the non-key-event and continue waiting */ ReadConsoleInput(handle, &inputrecord, 1, &length); - else - break; + continue; + } } - else - break; } - else - break; + /* there is some data available, stop waiting */ + break; } break; case FILE_TYPE_PIPE: + /* The handle represents an anonymous or named pipe, this means: + * - WaitForMultipleObjectsEx will always be signalled for it. + * - peek into the pipe and retrieve the amount of data available. + * + * Approach: Loop till either the internal event is signalled + * or there is data in the pipe available for reading. + */ while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) == WAIT_OBJECT_0 + 1) { + /* peek into the pipe and retrieve the amount of data available */ if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) { - if(length == 0) + /* if there is no data available, sleep and continue waiting */ + if(length == 0) { SleepEx(100, FALSE); - else - break; + continue; + } } else { - if(GetLastError() == ERROR_BROKEN_PIPE) + /* if the pipe has been closed, sleep and continue waiting */ + if(GetLastError() == ERROR_BROKEN_PIPE) { SleepEx(100, FALSE); - else - break; + continue; + } } + /* there is some data available, stop waiting */ + break; } break; default: + /* The handle has an unknown type, try to wait on it */ WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE); break; } return 0; } +static HANDLE select_ws_wait(HANDLE handle, HANDLE event) +{ + struct select_ws_wait_data *data; + HANDLE thread = NULL; + + /* allocate internal waiting data structure */ + data = malloc(sizeof(struct select_ws_wait_data)); + if(data) { + data->handle = handle; + data->event = event; + + /* launch waiting thread */ + thread = CreateThread(NULL, 0, + &select_ws_wait_thread, + data, 0, NULL); + + /* free data if thread failed to launch */ + if(!thread) { + free(data); + } + } + + return thread; +} static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { DWORD milliseconds, wait, idx; WSAEVENT wsaevent, *wsaevents; WSANETWORKEVENTS wsanetevents; - HANDLE handle, *handles; + HANDLE handle, *handles, *threads; curl_socket_t sock, *fdarr, *wsasocks; long networkevents; int error, fds; - HANDLE threadevent = NULL, threadhandle = NULL; - DWORD nfd = 0, wsa = 0; + HANDLE waitevent = NULL; + DWORD nfd = 0, thd = 0, wsa = 0; int ret = 0; /* check if the input value is valid */ @@ -616,6 +684,13 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, return 0; } + /* create internal event to signal waiting threads */ + waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!waitevent) { + errno = ENOMEM; + return -1; + } + /* allocate internal array for the original input handles */ fdarr = malloc(nfds * sizeof(curl_socket_t)); if(fdarr == NULL) { @@ -631,9 +706,19 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, return -1; } + /* allocate internal array for the internal threads handles */ + threads = malloc(nfds * sizeof(HANDLE)); + if(threads == NULL) { + free(handles); + free(fdarr); + errno = ENOMEM; + return -1; + } + /* allocate internal array for the internal socket handles */ wsasocks = malloc(nfds * sizeof(curl_socket_t)); if(wsasocks == NULL) { + free(threads); free(handles); free(fdarr); errno = ENOMEM; @@ -643,6 +728,7 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, /* allocate internal array for the internal WINSOCK2 events */ wsaevents = malloc(nfds * sizeof(WSAEVENT)); if(wsaevents == NULL) { + free(threads); free(wsasocks); free(handles); free(fdarr); @@ -668,11 +754,11 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, if(networkevents) { fdarr[nfd] = curlx_sitosk(fds); if(fds == fileno(stdin)) { - threadevent = CreateEvent(NULL, TRUE, FALSE, NULL); - threadhandle = CreateThread(NULL, 0, - &select_ws_stdin_wait_thread, - threadevent, 0, NULL); - handles[nfd] = threadhandle; + handle = GetStdHandle(STD_INPUT_HANDLE); + handle = select_ws_wait(handle, waitevent); + handles[nfd] = handle; + threads[thd] = handle; + thd++; } else if(fds == fileno(stdout)) { handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE); @@ -685,14 +771,19 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, if(wsaevent != WSA_INVALID_EVENT) { error = WSAEventSelect(fds, wsaevent, networkevents); if(error != SOCKET_ERROR) { - handles[nfd] = wsaevent; + handle = (HANDLE) wsaevent; + handles[nfd] = handle; wsasocks[wsa] = curlx_sitosk(fds); wsaevents[wsa] = wsaevent; wsa++; } else { - handles[nfd] = (HANDLE) curlx_sitosk(fds); WSACloseEvent(wsaevent); + handle = (HANDLE) curlx_sitosk(fds); + handle = select_ws_wait(handle, waitevent); + handles[nfd] = handle; + threads[thd] = handle; + thd++; } } } @@ -711,10 +802,8 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, /* wait for one of the internal handles to trigger */ wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, milliseconds, FALSE); - /* signal the event handle for the waiting thread */ - if(threadevent) { - SetEvent(threadevent); - } + /* signal the event handle for the waiting threads */ + SetEvent(waitevent); /* loop over the internal handles returned in the descriptors */ for(idx = 0; idx < nfd; idx++) { @@ -783,16 +872,16 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, WSACloseEvent(wsaevents[idx]); } - if(threadhandle) { - WaitForSingleObject(threadhandle, INFINITE); - CloseHandle(threadhandle); - } - if(threadevent) { - CloseHandle(threadevent); + for(idx = 0; idx < thd; idx++) { + WaitForSingleObject(threads[thd], INFINITE); + CloseHandle(threads[thd]); } + CloseHandle(waitevent); + free(wsaevents); free(wsasocks); + free(threads); free(handles); free(fdarr);