From 31b2a3e33f10941d252a7514539c6d11078497b5 Mon Sep 17 00:00:00 2001 From: redman Date: Thu, 22 Apr 1999 20:28:02 +0000 Subject: Improved socket driver, use WSAEventSelect on NT --- win/tclWinPort.h | 4 +- win/tclWinSock.c | 628 ++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 508 insertions(+), 124 deletions(-) diff --git a/win/tclWinPort.h b/win/tclWinPort.h index 4843383..aba6807 100644 --- a/win/tclWinPort.h +++ b/win/tclWinPort.h @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWinPort.h,v 1.8 1999/04/16 00:48:09 stanton Exp $ + * RCS: @(#) $Id: tclWinPort.h,v 1.9 1999/04/22 20:28:02 redman Exp $ */ #ifndef _TCLWINPORT @@ -56,7 +56,7 @@ typedef float *TCHAR; #include #include #include -#include +#include #define WIN32_LEAN_AND_MEAN #include diff --git a/win/tclWinSock.c b/win/tclWinSock.c index bfcc3a6..969e2bb 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWinSock.c,v 1.8 1999/04/16 00:48:09 stanton Exp $ + * RCS: @(#) $Id: tclWinSock.c,v 1.9 1999/04/22 20:28:02 redman Exp $ */ #include "tclWinInt.h" @@ -23,8 +23,22 @@ static int initialized = 0; static int hostnameInitialized = 0; static char hostname[255]; /* This buffer should be big enough for * hostname plus domain name. */ -TCL_DECLARE_MUTEX(socketMutex) +static int useThreads = 0; +static HANDLE socketThread; /* Thread used with WSAEventSelect to check + sockets for fileevents on NT (not used for + Win95 or Win98) */ +static WSAEVENT socketEvent; /* Event triggered by WSAEventSelect, for all + sockets, NT only */ +static WSAEVENT killEvent; /* Event used to kill off the socketThread, NT + only */ + +static CRITICAL_SECTION socketCS; /* Critical Section used even when building + without threads */ +TCL_DECLARE_MUTEX(socketMutex) /* Mutex used when built with multithreading + */ + + /* * The following structure contains pointers to all of the WinSock API entry * points used by Tcl. It is initialized by InitSockets. Since we @@ -73,6 +87,21 @@ static struct { int (PASCAL FAR *WSAGetLastError)(void); int (PASCAL FAR *WSAAsyncSelect)(SOCKET s, HWND hWnd, u_int wMsg, long lEvent); + + /* + * The following are used for the WinSock 2.0 for NT implementation + */ + + WSAEVENT (PASCAL FAR *WSACreateEvent)(void); + BOOL (PASCAL FAR *WSACloseEvent)(WSAEVENT event); + int (PASCAL FAR *WSAEventSelect)(SOCKET s, WSAEVENT event, long lEvent); + BOOL (PASCAL FAR *WSAResetEvent)(WSAEVENT event); + BOOL (PASCAL FAR *WSASetEvent)(WSAEVENT event); + int (PASCAL FAR *WSAEnumNetworkEvents)(SOCKET s, WSAEVENT event, + LPWSANETWORKEVENTS events); + DWORD (PASCAL FAR *WSAWaitForMultipleEvents)(DWORD cEvents, + const WSAEVENT FAR *events, + BOOL fWaitAll, DWORD dwTimeOUT, BOOL fAlertable); } winSock; /* @@ -96,7 +125,9 @@ typedef struct SocketInfo { * indicate which events are interesting. */ int readyEvents; /* OR'ed combination of FD_READ, FD_WRITE, * FD_CLOSE, FD_ACCEPT and FD_CONNECT that - * indicate which events have occurred. */ + * indicate which events have occurred. Not + * used for the WinSock 2.0 for NT + * implementation. */ int selectEvents; /* OR'ed combination of FD_READ, FD_WRITE, * FD_CLOSE, FD_ACCEPT and FD_CONNECT that * indicate which events are currently @@ -142,14 +173,26 @@ typedef struct SocketEvent { * for this socket */ typedef struct ThreadSpecificData { + SocketInfo *socketList; /* List of all sockets in the thread */ + /* - * Every open socket has an entry on the following list. + * Data need for the Win95/Win98 implementation: */ HWND hwnd; /* Handle to window for socket messages. */ - SocketInfo *socketList; + + /* + * Data need for the WinNT implementation: + */ + + HANDLE wakeEvent; /* Event to wake up the socket thread */ + Tcl_ThreadId threadId; /* ID of the thread */ + + struct ThreadSpecificData *next; /* Next entry in the thread list */ } ThreadSpecificData; +ThreadSpecificData *firstWaitingThread; /* List of threads */ + static Tcl_ThreadDataKey dataKey; /* @@ -171,6 +214,7 @@ static int SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr, static void SocketExitHandler _ANSI_ARGS_((ClientData clientData)); static LRESULT CALLBACK SocketProc _ANSI_ARGS_((HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)); +static DWORD WINAPI SocketThread(LPVOID arg); static void SocketSetupProc _ANSI_ARGS_((ClientData clientData, int flags)); static void SocketThreadExitHandler _ANSI_ARGS_((ClientData clientData)); @@ -218,6 +262,64 @@ static Tcl_ChannelType tcpChannelType = { #define WSA_VERSION_REQD MAKEWORD(1,1) + +/* + *---------------------------------------------------------------------- + * + * GetSocketState -- + * + * Retrieve the state of a given socket from the winsock API + * using WSAEnumNetWorkEvents. Modify the state appropriately + * when the winsock version is wrong. + * + * Assumes Mutex is held. + * + * Results: + * State of the socket in FD_* masks. + * + * Side effects: + * Modifies the infoPtr values to reflect the current state. + * + *---------------------------------------------------------------------- + */ + +int GetSocketState(SocketInfo *infoPtr) +{ + WSANETWORKEVENTS netEvents; + int event = 0; + + if (!useThreads) { + return infoPtr->readyEvents; + } + + EnterCriticalSection(&socketCS); + if ((*winSock.WSAEnumNetworkEvents)(infoPtr->socket, + socketEvent, &netEvents) == 0) { + + event = netEvents.lNetworkEvents; + + if (event & FD_CLOSE) { + event &= ~(FD_WRITE|FD_ACCEPT); + } + if (event & FD_CONNECT) { + /* + * The socket is now connected, so clear the + * async connect flag. + */ + + infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT); + + } + if(infoPtr->flags & SOCKET_ASYNC_CONNECT) { + infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT); + event |= FD_WRITE; + } + } + LeaveCriticalSection(&socketCS); + + return event; +} + /* *---------------------------------------------------------------------- * @@ -227,8 +329,6 @@ static Tcl_ChannelType tcpChannelType = { * library and set up the winSock function table. If successful, * registers the event window for the socket notifier code. * - * Assumes Mutex is held. - * * Results: * None. * @@ -244,12 +344,16 @@ static void InitSockets() { WSADATA wsaData; + DWORD id; OSVERSIONINFO info; static WNDCLASSA class; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); + Tcl_MutexLock(&socketMutex); if (! initialized) { + + InitializeCriticalSection(&socketCS); initialized = 1; Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL); @@ -261,15 +365,22 @@ InitSockets() GetVersionEx(&info); /* - * Check to see if Sockets are supported on this system. Since - * win32s panics if we call WSAStartup on a system that doesn't - * have winsock.dll, we need to look for it on the system first. - * If we find winsock, then load the library and initialize the - * stub table. + * Check to see if Sockets are supported on this system. + * + * For WinNT, use the WinSock 2.0 API to avoid creating extra windows + * to handle socket events. + * + * Not all versions of Win95 have WinSock 2.0, don't count on it being + * there. Also, until someone can get the code to work on Win98 or + * Win95 with WinSock 2.0, use the window-based version for those + * OS's. Major socket users (servers) will probably be on NT anyway. */ - if ((info.dwPlatformId != VER_PLATFORM_WIN32s) - || (SearchPathA(NULL, "WINSOCK", ".DLL", 0, NULL, NULL) != 0)) { + if ((info.dwPlatformId == VER_PLATFORM_WIN32_NT)&& + (SearchPathA(NULL, "ws2_32", ".dll", 0, NULL, NULL) != 0) ) { + useThreads = 1; + winSock.hInstance = LoadLibraryA("ws2_32.dll"); + } else if (SearchPathA(NULL, "wsock32", ".dll", 0, NULL, NULL) != 0) { winSock.hInstance = LoadLibraryA("wsock32.dll"); } else { winSock.hInstance = NULL; @@ -280,6 +391,7 @@ InitSockets() */ if (!SocketsEnabled()) { + Tcl_MutexUnlock(&socketMutex); return; } @@ -343,7 +455,8 @@ InitSockets() struct sockaddr FAR *name, int FAR *namelen)) GetProcAddress(winSock.hInstance, "getsockname"); winSock.WSAStartup = (int (PASCAL FAR *)(WORD wVersionRequired, - LPWSADATA lpWSAData)) GetProcAddress(winSock.hInstance, "WSAStartup"); + LPWSADATA lpWSAData)) GetProcAddress(winSock.hInstance, + "WSAStartup"); winSock.WSACleanup = (int (PASCAL FAR *)(void)) GetProcAddress(winSock.hInstance, "WSACleanup"); winSock.WSAGetLastError = (int (PASCAL FAR *)(void)) @@ -351,7 +464,31 @@ InitSockets() winSock.WSAAsyncSelect = (int (PASCAL FAR *)(SOCKET s, HWND hWnd, u_int wMsg, long lEvent)) GetProcAddress(winSock.hInstance, "WSAAsyncSelect"); - + + if (useThreads) { + winSock.WSACreateEvent = (WSAEVENT (PASCAL FAR *)(void)) + GetProcAddress(winSock.hInstance, "WSACreateEvent"); + winSock.WSACloseEvent = (BOOL (PASCAL FAR *)(WSAEVENT event)) + GetProcAddress(winSock.hInstance, "WSACloseEvent"); + winSock.WSAEventSelect = (int (PASCAL FAR *)(SOCKET s, + WSAEVENT event, + long lEvent)) + GetProcAddress(winSock.hInstance, "WSAEventSelect"); + winSock.WSAResetEvent = (BOOL (PASCAL FAR *)(WSAEVENT event)) + GetProcAddress(winSock.hInstance, "WSAResetEvent"); + winSock.WSASetEvent = (BOOL (PASCAL FAR *)(WSAEVENT event)) + GetProcAddress(winSock.hInstance, "WSASetEvent"); + winSock.WSAEnumNetworkEvents = (int (PASCAL FAR *)(SOCKET s, + WSAEVENT event, + LPWSANETWORKEVENTS events)) + GetProcAddress(winSock.hInstance, "WSAEnumNetworkEvents"); + winSock.WSAWaitForMultipleEvents = + (DWORD (PASCAL FAR *)(DWORD cEvents, + const WSAEVENT FAR *events, + BOOL fWaitAll, DWORD dwTimeOUT, BOOL fAlertable)) + GetProcAddress(winSock.hInstance, "WSAWaitForMultipleEvents"); + } + /* * Now check that all fields are properly initialized. If not, return * zero to indicate that we failed to initialize properly. @@ -385,6 +522,20 @@ InitSockets() (winSock.WSAGetLastError == NULL) || (winSock.WSAAsyncSelect == NULL)) { goto unloadLibrary; + } else if (useThreads && + ((winSock.WSACreateEvent == NULL) || + (winSock.WSACloseEvent == NULL) || + (winSock.WSAGetLastError == NULL) || + (winSock.WSAEventSelect == NULL) || + (winSock.WSAResetEvent == NULL) || + (winSock.WSASetEvent == NULL) || + (winSock.WSAEnumNetworkEvents == NULL) || + (winSock.WSAWaitForMultipleEvents == NULL))) { + /* + * WinSock 2.0 not correctly installed, use 1.x + */ + + useThreads = 0; } /* @@ -393,60 +544,85 @@ InitSockets() * us to get the wrong message number for socket events if the * message window is a subclass of a static control. */ - - class.style = 0; - class.cbClsExtra = 0; - class.cbWndExtra = 0; - class.hInstance = TclWinGetTclInstance(); - class.hbrBackground = NULL; - class.lpszMenuName = NULL; - class.lpszClassName = "TclSocket"; - class.lpfnWndProc = SocketProc; - class.hIcon = NULL; - class.hCursor = NULL; - - if (!RegisterClassA(&class)) { - TclWinConvertError(GetLastError()); - (*winSock.WSACleanup)(); - goto unloadLibrary; + if (!useThreads) { + class.style = 0; + class.cbClsExtra = 0; + class.cbWndExtra = 0; + class.hInstance = TclWinGetTclInstance(); + class.hbrBackground = NULL; + class.lpszMenuName = NULL; + class.lpszClassName = "TclSocket"; + class.lpfnWndProc = SocketProc; + class.hIcon = NULL; + class.hCursor = NULL; + + if (!RegisterClassA(&class)) { + TclWinConvertError(GetLastError()); + (*winSock.WSACleanup)(); + goto unloadLibrary; + } } /* * Initialize the winsock library and check the version number. */ - + if ((*winSock.WSAStartup)(WSA_VERSION_REQD, &wsaData) != 0) { - goto unloadLibrary; + goto unloadLibrary; } if (wsaData.wVersion != WSA_VERSION_REQD) { (*winSock.WSACleanup)(); goto unloadLibrary; } + + if (useThreads) { + socketEvent = (*winSock.WSACreateEvent)(); + killEvent = (*winSock.WSACreateEvent)(); + socketThread = CreateThread(NULL, 8000, SocketThread, + NULL, 0, &id); + SetThreadPriority(socketThread, THREAD_PRIORITY_HIGHEST); + } } + Tcl_MutexUnlock(&socketMutex); /* * Check for per-thread initialization. */ + EnterCriticalSection(&socketCS); if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->socketList = NULL; - tsdPtr->hwnd = CreateWindowA("TclSocket", "TclSocket", - WS_TILED, 0, 0, 0, 0, NULL, NULL, class.hInstance, NULL); + if (useThreads) { + tsdPtr->wakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (tsdPtr->hwnd == NULL) { - goto unloadLibrary; + } else { + tsdPtr->hwnd = CreateWindowA("TclSocket", "TclSocket", + WS_TILED, 0, 0, 0, 0, NULL, NULL, class.hInstance, NULL); + if (tsdPtr->hwnd == NULL) { + FreeLibrary(winSock.hInstance); + winSock.hInstance = NULL; + LeaveCriticalSection(&socketCS); + return; + } } Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL); Tcl_CreateThreadExitHandler(SocketThreadExitHandler, NULL); + + tsdPtr->threadId = Tcl_GetCurrentThread(); + tsdPtr->next = firstWaitingThread; + firstWaitingThread = tsdPtr; + } + LeaveCriticalSection(&socketCS); return; unloadLibrary: FreeLibrary(winSock.hInstance); winSock.hInstance = NULL; + Tcl_MutexUnlock(&socketMutex); return; } @@ -471,9 +647,9 @@ static int SocketsEnabled() { int enabled; - Tcl_MutexLock(&socketMutex); + EnterCriticalSection(&socketCS); enabled = (winSock.hInstance != NULL); - Tcl_MutexUnlock(&socketMutex); + LeaveCriticalSection(&socketCS); return enabled; } @@ -500,16 +676,27 @@ static void SocketExitHandler(clientData) ClientData clientData; /* Not used. */ { - Tcl_MutexLock(&socketMutex); + EnterCriticalSection(&socketCS); + if (useThreads && (socketThread != NULL)) { + LeaveCriticalSection(&socketCS); + (*winSock.WSASetEvent)(killEvent); + WaitForSingleObject(socketThread, INFINITE); + EnterCriticalSection(&socketCS); + CloseHandle(socketThread); + (*winSock.WSACloseEvent)(killEvent); + (*winSock.WSACloseEvent)(socketEvent); + } if (winSock.hInstance) { - UnregisterClassA("TclSocket", TclWinGetTclInstance()); + if (!useThreads) { + UnregisterClassA("TclSocket", TclWinGetTclInstance()); + } (*winSock.WSACleanup)(); FreeLibrary(winSock.hInstance); winSock.hInstance = NULL; } initialized = 0; hostnameInitialized = 0; - Tcl_MutexUnlock(&socketMutex); + LeaveCriticalSection(&socketCS); } /* @@ -534,10 +721,31 @@ static void SocketThreadExitHandler(clientData) ClientData clientData; /* Not used. */ { - ThreadSpecificData *tsdPtr = - (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); + ThreadSpecificData *tsdPtr, *nextPtr; + + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); - DestroyWindow(tsdPtr->hwnd); + if (useThreads) { + EnterCriticalSection(&socketCS); + CloseHandle(tsdPtr->wakeEvent); + + if (firstWaitingThread == tsdPtr) { + firstWaitingThread = tsdPtr->next; + } else { + for (nextPtr = firstWaitingThread; + nextPtr != NULL; + nextPtr = nextPtr->next) { + + if (nextPtr->next == tsdPtr) { + nextPtr->next = tsdPtr->next; + break; + } + } + } + LeaveCriticalSection(&socketCS); + } else { + DestroyWindow(tsdPtr->hwnd); + } Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL); } @@ -565,9 +773,7 @@ int TclpHasSockets(interp) Tcl_Interp *interp; { - Tcl_MutexLock(&socketMutex); InitSockets(); - Tcl_MutexUnlock(&socketMutex); if (SocketsEnabled()) { return TCL_OK; @@ -604,7 +810,8 @@ SocketSetupProc(data, flags) SocketInfo *infoPtr; Tcl_Time blockTime = { 0, 0 }; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - + int readyEvents; + if (!(flags & TCL_FILE_EVENTS)) { return; } @@ -613,13 +820,16 @@ SocketSetupProc(data, flags) * Check to see if there is a ready socket. If so, poll. */ + EnterCriticalSection(&socketCS); for (infoPtr = tsdPtr->socketList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if (infoPtr->readyEvents & infoPtr->watchEvents) { + readyEvents = GetSocketState(infoPtr); + if (readyEvents & infoPtr->watchEvents) { Tcl_SetMaxBlockTime(&blockTime); break; } } + LeaveCriticalSection(&socketCS); } /* @@ -644,10 +854,11 @@ SocketCheckProc(data, flags) ClientData data; /* Not used. */ int flags; /* Event flags as passed to Tcl_DoOneEvent. */ { + int readyEvents; SocketInfo *infoPtr; SocketEvent *evPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - + if (!(flags & TCL_FILE_EVENTS)) { return; } @@ -658,17 +869,22 @@ SocketCheckProc(data, flags) * events). */ + EnterCriticalSection(&socketCS); for (infoPtr = tsdPtr->socketList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if ((infoPtr->readyEvents & infoPtr->watchEvents) + readyEvents = GetSocketState(infoPtr); + if ((readyEvents & infoPtr->watchEvents) && !(infoPtr->flags & SOCKET_PENDING)) { infoPtr->flags |= SOCKET_PENDING; evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent)); evPtr->header.proc = SocketEventProc; evPtr->socket = infoPtr->socket; + LeaveCriticalSection(&socketCS); Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); + EnterCriticalSection(&socketCS); } } + LeaveCriticalSection(&socketCS); } /* @@ -702,6 +918,7 @@ SocketEventProc(evPtr, flags) SocketEvent *eventPtr = (SocketEvent *) evPtr; int mask = 0; int events; + int readyEvents; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { @@ -712,12 +929,14 @@ SocketEventProc(evPtr, flags) * Find the specified socket on the socket list. */ + EnterCriticalSection(&socketCS); for (infoPtr = tsdPtr->socketList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->socket == eventPtr->socket) { break; } } + LeaveCriticalSection(&socketCS); /* * Discard events that have gone stale. @@ -733,7 +952,8 @@ SocketEventProc(evPtr, flags) * Handle connection requests directly. */ - if (infoPtr->readyEvents & FD_ACCEPT) { + readyEvents = GetSocketState(infoPtr); + if (readyEvents & FD_ACCEPT) { TcpAccept(infoPtr); return 1; } @@ -744,7 +964,7 @@ SocketEventProc(evPtr, flags) * we can notify the channel. */ - events = infoPtr->readyEvents & infoPtr->watchEvents; + events = readyEvents & infoPtr->watchEvents; if (events & FD_CLOSE) { /* @@ -772,8 +992,11 @@ SocketEventProc(evPtr, flags) * async select handler and keep waiting. */ - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, 0, 0); - + if (!useThreads) { + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, + tsdPtr->hwnd, 0, 0); + } + FD_ZERO(&readFds); FD_SET(infoPtr->socket, &readFds); timeout.tv_usec = 0; @@ -781,11 +1004,11 @@ SocketEventProc(evPtr, flags) if ((*winSock.select)(0, &readFds, NULL, NULL, &timeout) != 0) { mask |= TCL_READABLE; - } else { + } else if (!useThreads) { (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, SOCKET_MESSAGE, infoPtr->selectEvents); - infoPtr->readyEvents &= ~(FD_READ); } + infoPtr->readyEvents &= ~(FD_READ); } if (events & (FD_WRITE | FD_CONNECT)) { mask |= TCL_WRITABLE; @@ -883,6 +1106,7 @@ TcpCloseProc(instanceData, interp) * Remove the socket from socketList. */ + EnterCriticalSection(&socketCS); for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL; nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { if ((*nextPtrPtr) == infoPtr) { @@ -890,6 +1114,7 @@ TcpCloseProc(instanceData, interp) break; } } + LeaveCriticalSection(&socketCS); ckfree((char *) infoPtr); return errorCode; @@ -928,8 +1153,10 @@ NewSocketInfo(socket) infoPtr->acceptProc = NULL; infoPtr->lastError = 0; + EnterCriticalSection(&socketCS); infoPtr->nextPtr = tsdPtr->socketList; tsdPtr->socketList = infoPtr; + LeaveCriticalSection(&socketCS); return infoPtr; } @@ -1046,8 +1273,10 @@ CreateSocket(interp, port, host, server, myaddr, myport, async) * Set up the select mask for connection request events. */ + EnterCriticalSection(&socketCS); infoPtr->selectEvents = FD_ACCEPT; infoPtr->watchEvents |= FD_ACCEPT; + LeaveCriticalSection(&socketCS); } else { @@ -1102,11 +1331,13 @@ CreateSocket(interp, port, host, server, myaddr, myport, async) * attempt has not completed, include connect events. */ + EnterCriticalSection(&socketCS); infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE; if (asyncConnect) { infoPtr->flags |= SOCKET_ASYNC_CONNECT; infoPtr->selectEvents |= FD_CONNECT; } + LeaveCriticalSection(&socketCS); } /* @@ -1114,10 +1345,15 @@ CreateSocket(interp, port, host, server, myaddr, myport, async) * automatically places the socket into non-blocking mode. */ - (*winSock.ioctlsocket)(sock, FIONBIO, &flag); - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, - SOCKET_MESSAGE, infoPtr->selectEvents); - + if (useThreads) { + (void) (*winSock.WSAEventSelect)(infoPtr->socket, socketEvent, + infoPtr->selectEvents); + } else { + (*winSock.ioctlsocket)(sock, FIONBIO, &flag); + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, + SOCKET_MESSAGE, infoPtr->selectEvents); + } + return infoPtr; error: @@ -1231,6 +1467,7 @@ WaitForSocketEvent(infoPtr, events, errorCodePtr) int *errorCodePtr; /* Where to store errors? */ { MSG msg; + int readyEvents; int result = 1; int oldMode; ThreadSpecificData *tsdPtr = @@ -1239,47 +1476,67 @@ WaitForSocketEvent(infoPtr, events, errorCodePtr) /* * Be sure to disable event servicing so we are truly modal. */ - - oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE); - /* - * Reset WSAAsyncSelect so we have a fresh set of events pending. - */ - - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, 0, 0); - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, - SOCKET_MESSAGE, infoPtr->selectEvents); - - while (1) { - /* - * Process all outstanding messages on the socket window. - */ + oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE); + + if (useThreads) { - while (PeekMessage(&msg, tsdPtr->hwnd, 0, 0, PM_REMOVE)) { - DispatchMessage(&msg); + while (1) { + readyEvents = GetSocketState(infoPtr); + if (infoPtr->lastError) { + *errorCodePtr = infoPtr->lastError; + result = 0; + break; + } else if (readyEvents & events) { + break; + } else if (infoPtr->flags & SOCKET_ASYNC) { + *errorCodePtr = EWOULDBLOCK; + result = 0; + break; + } + WaitForSingleObject(tsdPtr->wakeEvent, 100); +// Tcl_ServiceAll(); } + } else { - if (infoPtr->lastError) { - *errorCodePtr = infoPtr->lastError; - result = 0; - break; - } else if (infoPtr->readyEvents & events) { - break; - } else if (infoPtr->flags & SOCKET_ASYNC) { - *errorCodePtr = EWOULDBLOCK; - result = 0; - break; - } - /* - * Wait until something happens. + * Reset WSAAsyncSelect so we have a fresh set of events pending. */ - WaitMessage(); - } - + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, 0, 0); + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, + SOCKET_MESSAGE, infoPtr->selectEvents); + while (1) { + /* + * Process all outstanding messages on the socket window. + */ + + while (PeekMessage(&msg, tsdPtr->hwnd, 0, 0, PM_REMOVE)) { + DispatchMessage(&msg); + } + + if (infoPtr->lastError) { + *errorCodePtr = infoPtr->lastError; + result = 0; + break; + } else if (infoPtr->readyEvents & events) { + break; + } else if (infoPtr->flags & SOCKET_ASYNC) { + *errorCodePtr = EWOULDBLOCK; + result = 0; + break; + } + + /* + * Wait until something happens. + */ + + WaitMessage(); + } + } (void) Tcl_SetServiceMode(oldMode); + return result; } @@ -1382,14 +1639,24 @@ Tcl_MakeTcpClientChannel(sock) infoPtr = NewSocketInfo((SOCKET) sock); + if (infoPtr == NULL) { + return NULL; + } + /* * Start watching for read/write events on the socket. */ infoPtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE; - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, - SOCKET_MESSAGE, infoPtr->selectEvents); + if (useThreads) { + (void) (*winSock.WSAEventSelect)(infoPtr->socket, socketEvent, + infoPtr->selectEvents); + } else { + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, + SOCKET_MESSAGE, infoPtr->selectEvents); + } + wsprintfA(channelName, "sock%d", infoPtr->socket); infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE)); @@ -1500,7 +1767,7 @@ TcpAccept(infoPtr) */ infoPtr->readyEvents &= ~(FD_ACCEPT); - + if (newSocket == INVALID_SOCKET) { return; } @@ -1523,9 +1790,14 @@ TcpAccept(infoPtr) */ newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE); - (void) (*winSock.WSAAsyncSelect)(newInfoPtr->socket, tsdPtr->hwnd, - SOCKET_MESSAGE, newInfoPtr->selectEvents); - + if (useThreads) { + (void) (*winSock.WSAEventSelect)(newInfoPtr->socket, socketEvent, + newInfoPtr->selectEvents); + } else { + (void) (*winSock.WSAAsyncSelect)(newInfoPtr->socket, tsdPtr->hwnd, + SOCKET_MESSAGE, newInfoPtr->selectEvents); + } + wsprintfA(channelName, "sock%d", newInfoPtr->socket); newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) newInfoPtr, (TCL_READABLE | TCL_WRITABLE)); @@ -1578,6 +1850,7 @@ TcpInputProc(instanceData, buf, toRead, errorCodePtr) SocketInfo *infoPtr = (SocketInfo *) instanceData; int bytesRead; int error; + int readyEvents; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); @@ -1607,7 +1880,13 @@ TcpInputProc(instanceData, buf, toRead, errorCodePtr) /* * Check to see if the socket is connected before trying to read. */ + infoPtr->selectEvents |= FD_READ | FD_CLOSE; + if (useThreads) { + (void) (*winSock.WSAEventSelect)(infoPtr->socket, socketEvent, + infoPtr->selectEvents); + } + if ((infoPtr->flags & SOCKET_ASYNC_CONNECT) && ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) { return -1; @@ -1622,11 +1901,15 @@ TcpInputProc(instanceData, buf, toRead, errorCodePtr) */ while (1) { - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, - 0, 0); + + if (!useThreads) { + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, + 0, 0); + } bytesRead = (*winSock.recv)(infoPtr->socket, buf, toRead, 0); + infoPtr->readyEvents &= ~(FD_READ); - + /* * Check for end-of-file condition or successful read. */ @@ -1642,8 +1925,9 @@ TcpInputProc(instanceData, buf, toRead, errorCodePtr) * If an error occurs after the FD_CLOSE has arrived, * then ignore the error and report an EOF. */ - - if (infoPtr->readyEvents & FD_CLOSE) { + readyEvents = GetSocketState(infoPtr); + + if (readyEvents & FD_CLOSE) { infoPtr->flags |= SOCKET_EOF; bytesRead = 0; break; @@ -1654,7 +1938,8 @@ TcpInputProc(instanceData, buf, toRead, errorCodePtr) */ error = (*winSock.WSAGetLastError)(); - if ((infoPtr->flags & SOCKET_ASYNC) || (error != WSAEWOULDBLOCK)) { + if ((error != 0) && ((infoPtr->flags & SOCKET_ASYNC) + || (error != WSAEWOULDBLOCK))) { TclWinConvertWSAError(error); *errorCodePtr = Tcl_GetErrno(); bytesRead = -1; @@ -1671,9 +1956,11 @@ TcpInputProc(instanceData, buf, toRead, errorCodePtr) break; } } - - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, - SOCKET_MESSAGE, infoPtr->selectEvents); + + if (!useThreads) { + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, + SOCKET_MESSAGE, infoPtr->selectEvents); + } return bytesRead; } @@ -1731,20 +2018,23 @@ TcpOutputProc(instanceData, buf, toWrite, errorCodePtr) } while (1) { - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, - 0, 0); + if (!useThreads) { + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, + 0, 0); + } + bytesWritten = (*winSock.send)(infoPtr->socket, buf, toWrite, 0); if (bytesWritten != SOCKET_ERROR) { - /* - * Since Windows won't generate a new write event until we hit - * an overflow condition, we need to force the event loop to - * poll until the condition changes. - */ + if (!useThreads && (infoPtr->watchEvents & FD_WRITE)) { + /* + * Since Windows won't generate a new write event until we hit + * an overflow condition, we need to force the event loop to + * poll until the condition changes. + */ - if (infoPtr->watchEvents & FD_WRITE) { Tcl_Time blockTime = { 0, 0 }; Tcl_SetMaxBlockTime(&blockTime); - } + } break; } @@ -1756,6 +2046,7 @@ TcpOutputProc(instanceData, buf, toWrite, errorCodePtr) */ error = (*winSock.WSAGetLastError)(); + if (error == WSAEWOULDBLOCK) { infoPtr->readyEvents &= ~(FD_WRITE); if (infoPtr->flags & SOCKET_ASYNC) { @@ -1781,8 +2072,10 @@ TcpOutputProc(instanceData, buf, toWrite, errorCodePtr) } } - (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, - SOCKET_MESSAGE, infoPtr->selectEvents); + if (!useThreads) { + (void) (*winSock.WSAAsyncSelect)(infoPtr->socket, tsdPtr->hwnd, + SOCKET_MESSAGE, infoPtr->selectEvents); + } return bytesWritten; } @@ -1981,12 +2274,14 @@ TcpWatchProc(instanceData, mask) * combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. */ { + int readyEvents; SocketInfo *infoPtr = (SocketInfo *) instanceData; /* * Update the watch events mask. */ - + + EnterCriticalSection(&socketCS); infoPtr->watchEvents = 0; if (mask & TCL_READABLE) { infoPtr->watchEvents |= (FD_READ|FD_CLOSE|FD_ACCEPT); @@ -1994,13 +2289,15 @@ TcpWatchProc(instanceData, mask) if (mask & TCL_WRITABLE) { infoPtr->watchEvents |= (FD_WRITE|FD_CONNECT); } + LeaveCriticalSection(&socketCS); /* * If there are any conditions already set, then tell the notifier to poll * rather than block. */ - if (infoPtr->readyEvents & infoPtr->watchEvents) { + readyEvents = GetSocketState(infoPtr); + if (readyEvents & infoPtr->watchEvents) { Tcl_Time blockTime = { 0, 0 }; Tcl_SetMaxBlockTime(&blockTime); } @@ -2038,6 +2335,93 @@ TcpGetHandleProc(instanceData, direction, handlePtr) /* *---------------------------------------------------------------------- * + * SocketThread -- + * + * + * Results: + * 0 on success. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ + +static DWORD WINAPI +SocketThread(LPVOID arg) +{ + SocketInfo *infoPtr; + WSAEVENT events[2]; + ThreadSpecificData *tsdPtr; + DWORD result; + + /* + * Find the specified socket on the socket list and update its + * eventState flag. + */ + + events[0] = killEvent; + events[1] = socketEvent; + + while (1) { + + result = (*winSock.WSAWaitForMultipleEvents)(2, events, FALSE, + WSA_INFINITE, FALSE); + + switch (result) { + case (WSA_WAIT_EVENT_0 +1): + /* + * A socket event has fired, determine which socket(s) it was + * and which thread(s) it came from. + */ + + EnterCriticalSection(&socketCS); + for (tsdPtr = firstWaitingThread; tsdPtr != NULL; + tsdPtr = tsdPtr->next) { + + for (infoPtr = tsdPtr->socketList; infoPtr != NULL; + infoPtr = infoPtr->nextPtr) { + + if (GetSocketState(infoPtr) & infoPtr->watchEvents) { + Tcl_ThreadAlert(tsdPtr->threadId); + SetEvent(tsdPtr->wakeEvent); + + /* + * Only process one at a time for a given thread, + * so break here. + */ + + break; + } + } + } + LeaveCriticalSection(&socketCS); + (*winSock.WSAResetEvent)(socketEvent); + break; + + case WSA_WAIT_EVENT_0: + /* + * This thread is supposed to kill itself off + */ + + return 0; + break; + + default: + /* + * The thread has detected a fatal error + */ + + return 1; + break; + } + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * * SocketProc -- * * This function is called when WSAAsyncSelect has been used -- cgit v0.12