summaryrefslogtreecommitdiffstats
path: root/win/tclWinSock.c
diff options
context:
space:
mode:
authorredman <redman>1999-04-22 20:28:02 (GMT)
committerredman <redman>1999-04-22 20:28:02 (GMT)
commit31b2a3e33f10941d252a7514539c6d11078497b5 (patch)
tree2896319ca9c9283d121612aca3e37c596b6aab62 /win/tclWinSock.c
parentf0d1d4682e8beee348c48de18e4622de4080536c (diff)
downloadtcl-31b2a3e33f10941d252a7514539c6d11078497b5.zip
tcl-31b2a3e33f10941d252a7514539c6d11078497b5.tar.gz
tcl-31b2a3e33f10941d252a7514539c6d11078497b5.tar.bz2
Improved socket driver, use WSAEventSelect on NT
Diffstat (limited to 'win/tclWinSock.c')
-rw-r--r--win/tclWinSock.c628
1 files changed, 506 insertions, 122 deletions
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