Subject: file tclWinSock.c reorganized to minimize diff to tclUnixSock.c. No functional change

From: oehhar <[email-masked]>
Date: Fri, 14 Mar 2014 16:59:25 +0000

---
 win/tclWinSock.c | 3675 +++++++++++++++++++++++++++---------------------------
 1 file changed, 1859 insertions(+), 1816 deletions(-)

diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index d9b9789..f65ea46 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -9,6 +9,9 @@
  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
  * -----------------------------------------------------------------------
+ * The order and naming of functions in this file should minimize
+ * the file diff to tclUnixSock.c.
+ * -----------------------------------------------------------------------
  *
  * General information on how this module works.
  *
@@ -50,7 +53,7 @@
 //#define DEBUGGING
 #ifdef DEBUGGING
 #define DEBUG(x) fprintf(stderr, ">>> %p %s(%d): %s<<<\n", \
-			    infoPtr, __FUNCTION__, __LINE__, x)
+			    statePtr, __FUNCTION__, __LINE__, x)
 #else
 #define DEBUG(x)
 #endif
@@ -82,6 +85,15 @@
 #undef getsockopt
 #undef setsockopt
 
+/*
+ * Helper macros to make parts of this file clearer. The macros do exactly
+ * what they say on the tin. :-) They also only ever refer to their arguments
+ * once, and so can be used without regard to side effects.
+ */
+
+#define SET_BITS(var, bits)	((var) |= (bits))
+#define CLEAR_BITS(var, bits)	((var) &= ~(bits))
+
 /* "sock" + a pointer in hex + \0 */
 #define SOCK_CHAN_LENGTH        (4 + sizeof(void *) * 2 + 1)
 #define SOCK_TEMPLATE           "sock%p"
@@ -97,15 +109,6 @@ static const TCHAR classname[] = TEXT("TclSocket");
 TCL_DECLARE_MUTEX(socketMutex)
 
 /*
- * The following variable holds the network name of this host.
- */
-
-static TclInitProcessGlobalValueProc InitializeHostName;
-static ProcessGlobalValue hostName = {
-    0, 0, NULL, NULL, InitializeHostName, NULL, NULL
-};
-
-/*
  * The following defines declare the messages used on socket windows.
  */
 
@@ -131,20 +134,19 @@ typedef union {
 #define IN6_ARE_ADDR_EQUAL IN6_ADDR_EQUAL
 #endif
 
-typedef struct SocketInfo SocketInfo;
+/*
+ * This structure describes per-instance state of a tcp based channel.
+ */
+
+typedef struct TcpState TcpState;
 
 typedef struct TcpFdList {
-    SocketInfo *infoPtr;
+    TcpState *statePtr;
     SOCKET fd;
     struct TcpFdList *next;
 } TcpFdList;
 
-/*
- * The following structure is used to store the data associated with each
- * socket.
- */
-
-struct SocketInfo {
+struct TcpState {
     Tcl_Channel channel;	/* Channel associated with this socket. */
     struct TcpFdList *sockets;	/* Windows SOCKET handle. */
     int flags;			/* Bit field comprised of the flags described
@@ -154,28 +156,55 @@ 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.
+				 * Set by notifier thread, access must be
+				 * protected by semaphore */
     int selectEvents;		/* OR'ed combination of FD_READ, FD_WRITE,
 				 * FD_CLOSE, FD_ACCEPT and FD_CONNECT that
 				 * indicate which events are currently being
 				 * selected. */
     int acceptEventCount;	/* Count of the current number of FD_ACCEPTs
-				 * that have arrived and not yet processed. */
+				 * that have arrived and not yet processed.
+				 * Set by notifier thread, access must be
+				 * protected by semaphore */
     Tcl_TcpAcceptProc *acceptProc;
 				/* Proc to call on accept. */
     ClientData acceptProcData;	/* The data for the accept proc. */
+
+    /*
+     * Only needed for client sockets
+     */
+
     struct addrinfo *addrlist;	/* Addresses to connect to. */
     struct addrinfo *addr;	/* Iterator over addrlist. */
     struct addrinfo *myaddrlist;/* Local address. */
     struct addrinfo *myaddr;	/* Iterator over myaddrlist. */
     int status;                 /* Cache status of async socket. */
     int cachedBlocking;         /* Cache blocking mode of async socket. */
-    int lastError;		/* Error code from last message. */
-    struct SocketInfo *nextPtr;	/* The next socket on the per-thread socket
+    int lastError;		/* Error code from last message.
+				 * Set by notifier thread, access must be
+				 * protected by semaphore */
+    struct TcpState *nextPtr;	/* The next socket on the per-thread socket
 				 * list. */
 };
 
 /*
+ * These bits may be ORed together into the "flags" field of a TcpState
+ * structure.
+ */
+
+#define TCP_ASYNC_SOCKET	(1<<0)	/* Asynchronous socket. */
+#define TCP_ASYNC_CONNECT	(1<<1)	/* Async connect in progress. */
+#define SOCKET_EOF		(1<<2)	/* A zero read happened on the
+					 * socket. */
+#define SOCKET_PENDING		(1<<3)	/* A message has been sent for this
+					 * socket */
+#define SOCKET_REENTER_PENDING	(1<<4)	/* CreateClientSocket was called to
+					 * process an async connect. This
+					 * flag indicates that reentry is
+					 * still pending */
+
+/*
  * The following structure is what is added to the Tcl event queue when a
  * socket event occurs.
  */
@@ -184,8 +213,8 @@ typedef struct {
     Tcl_Event header;		/* Information that is standard for all
 				 * events. */
     SOCKET socket;		/* Socket descriptor that is ready. Used to
-				 * find the SocketInfo structure for the file
-				 * (can't point directly to the SocketInfo
+				 * find the TcpState structure for the file
+				 * (can't point directly to the TcpState
 				 * structure because it could go away while
 				 * the event is queued). */
 } SocketEvent;
@@ -196,20 +225,6 @@ typedef struct {
 
 #define TCP_BUFFER_SIZE 4096
 
-/*
- * The following macros may be used to set the flags field of a SocketInfo
- * structure.
- */
-
-#define TCP_ASYNC_SOCKET	(1<<0)	/* The socket is in blocking mode. */
-#define SOCKET_EOF		(1<<1)	/* A zero read happened on the
-					 * socket. */
-#define SOCKET_ASYNC_CONNECT	(1<<2)	/* This socket uses async connect. */
-#define SOCKET_PENDING		(1<<3)	/* A message has been sent for this
-					 * socket */
-#define SOCKET_REENTER_PENDING	(1<<4)	/* The reentering after a received
-					 * FD_CONNECT to CreateClientSocket
-					 * is pending */
 
 typedef struct {
     HWND hwnd;			/* Handle to window for socket messages. */
@@ -220,11 +235,11 @@ typedef struct {
 				 * socketThread has been initialized and has
 				 * started. */
     HANDLE socketListLock;	/* Win32 Event to lock the socketList */
-    SocketInfo *pendingSocketInfo;
+    TcpState *pendingTcpState;
 				/* This socket is opened but not jet in the
 				 * list. This value is also checked by
 				 * the event structure. */
-    SocketInfo *socketList;	/* Every open socket in this thread has an
+    TcpState *socketList;	/* Every open socket in this thread has an
 				 * entry on this list. */
 } ThreadSpecificData;
 
@@ -232,22 +247,24 @@ static Tcl_ThreadDataKey dataKey;
 static WNDCLASS windowClass;
 
 /*
- * Static functions defined in this file.
+ * Static routines for this file:
  */
 
-static int		CreateClientSocket(Tcl_Interp *interp, SocketInfo *infoPtr);
+static int		CreateClientSocket(Tcl_Interp *interp,
+			    TcpState *state);
 static void		InitSockets(void);
-static SocketInfo *	NewSocketInfo(SOCKET socket);
+static TcpState *	NewSocketInfo(SOCKET socket);
 static void		SocketExitHandler(ClientData clientData);
 static LRESULT CALLBACK	SocketProc(HWND hwnd, UINT message, WPARAM wParam,
 			    LPARAM lParam);
 static int		SocketsEnabled(void);
 static void		TcpAccept(TcpFdList *fds, SOCKET newSocket, address addr);
-static int		WaitForConnect(SocketInfo *infoPtr, int *errorCodePtr,
+static int		WaitForConnect(TcpState *statePtr, int *errorCodePtr,
 			    int terminate_connect);
-static int		WaitForSocketEvent(SocketInfo *infoPtr, int events,
+static int		WaitForSocketEvent(TcpState *statePtr, int events,
 			    int *errorCodePtr);
-static int		FindFDInList(SocketInfo *infoPtr, SOCKET socket);
+static void		AddSocketInfoFd(TcpState *statePtr,  SOCKET socket);
+static int		FindFDInList(TcpState *statePtr, SOCKET socket);
 static DWORD WINAPI	SocketThread(LPVOID arg);
 static void		TcpThreadActionProc(ClientData instanceData,
 			    int action);
@@ -255,7 +272,7 @@ static void		TcpThreadActionProc(ClientData instanceData,
 static Tcl_EventCheckProc	SocketCheckProc;
 static Tcl_EventProc		SocketEventProc;
 static Tcl_EventSetupProc	SocketSetupProc;
-static Tcl_DriverBlockModeProc	TcpBlockProc;
+static Tcl_DriverBlockModeProc	TcpBlockModeProc;
 static Tcl_DriverCloseProc	TcpCloseProc;
 static Tcl_DriverClose2Proc	TcpClose2Proc;
 static Tcl_DriverSetOptionProc	TcpSetOptionProc;
@@ -267,28 +284,37 @@ static Tcl_DriverGetHandleProc	TcpGetHandleProc;
 
 /*
  * This structure describes the channel type structure for TCP socket
- * based IO.
+ * based IO:
  */
 
 static const Tcl_ChannelType tcpChannelType = {
-    "tcp",		    /* Type name. */
-    TCL_CHANNEL_VERSION_5,  /* v5 channel */
-    TcpCloseProc,	    /* Close proc. */
-    TcpInputProc,	    /* Input proc. */
-    TcpOutputProc,	    /* Output proc. */
-    NULL,		    /* Seek proc. */
-    TcpSetOptionProc,	    /* Set option proc. */
-    TcpGetOptionProc,	    /* Get option proc. */
-    TcpWatchProc,	    /* Set up notifier to watch this channel. */
-    TcpGetHandleProc,	    /* Get an OS handle from channel. */
-    TcpClose2Proc,	    /* Close2proc. */
-    TcpBlockProc,	    /* Set socket into (non-)blocking mode. */
-    NULL,		    /* flush proc. */
-    NULL,		    /* handler proc. */
-    NULL,		    /* wide seek proc */
-    TcpThreadActionProc,    /* thread action proc */
-    NULL		    /* truncate */
+    "tcp",			/* Type name. */
+    TCL_CHANNEL_VERSION_5,	/* v5 channel */
+    TcpCloseProc,		/* Close proc. */
+    TcpInputProc,		/* Input proc. */
+    TcpOutputProc,		/* Output proc. */
+    NULL,			/* Seek proc. */
+    TcpSetOptionProc,		/* Set option proc. */
+    TcpGetOptionProc,		/* Get option proc. */
+    TcpWatchProc,		/* Initialize notifier. */
+    TcpGetHandleProc,		/* Get OS handles out of channel. */
+    TcpClose2Proc,		/* Close2 proc. */
+    TcpBlockModeProc,		/* Set blocking or non-blocking mode.*/
+    NULL,			/* flush proc. */
+    NULL,			/* handler proc. */
+    NULL,			/* wide seek proc. */
+    TcpThreadActionProc,	/* thread action proc. */
+    NULL			/* truncate proc. */
 };
+
+/*
+ * The following variable holds the network name of this host.
+ */
+
+static TclInitProcessGlobalValueProc InitializeHostName;
+static ProcessGlobalValue hostName =
+	{0, 0, NULL, NULL, InitializeHostName, NULL, NULL};
+
 void printaddrinfo(struct addrinfo *ai, char *prefix)
 {
     char host[NI_MAXHOST], port[NI_MAXSERV];
@@ -311,202 +337,125 @@ void printaddrinfolist(struct addrinfo *addrlist, char *prefix)
 /*
  *----------------------------------------------------------------------
  *
- * InitSockets --
- *
- *	Initialize the socket module.  If winsock startup is successful,
- *	registers the event window for the socket notifier code.
+ * InitializeHostName --
  *
- *	Assumes socketMutex is held.
+ *	This routine sets the process global value of the name of the local
+ *	host on which the process is running.
  *
  * Results:
  *	None.
  *
- * Side effects:
- *	Initializes winsock, registers a new window class and creates a
- *	window for use in asynchronous socket notification.
- *
  *----------------------------------------------------------------------
  */
 
-static void
-InitSockets(void)
+void
+InitializeHostName(
+    char **valuePtr,
+    int *lengthPtr,
+    Tcl_Encoding *encodingPtr)
 {
-    DWORD id, err;
-    WSADATA wsaData;
-    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
-
-    if (!initialized) {
-	initialized = 1;
-	TclCreateLateExitHandler(SocketExitHandler, NULL);
+    TCHAR tbuf[MAX_COMPUTERNAME_LENGTH + 1];
+    DWORD length = MAX_COMPUTERNAME_LENGTH + 1;
+    Tcl_DString ds;
 
+    if (GetComputerName(tbuf, &length) != 0) {
 	/*
-	 * Create the async notification window with a new class. We must
-	 * create a new class to avoid a Windows 95 bug that causes us to get
-	 * the wrong message number for socket events if the message window is
-	 * a subclass of a static control.
+	 * Convert string from native to UTF then change to lowercase.
 	 */
 
-	windowClass.style = 0;
-	windowClass.cbClsExtra = 0;
-	windowClass.cbWndExtra = 0;
-	windowClass.hInstance = TclWinGetTclInstance();
-	windowClass.hbrBackground = NULL;
-	windowClass.lpszMenuName = NULL;
-	windowClass.lpszClassName = classname;
-	windowClass.lpfnWndProc = SocketProc;
-	windowClass.hIcon = NULL;
-	windowClass.hCursor = NULL;
-
-	if (!RegisterClass(&windowClass)) {
-	    TclWinConvertError(GetLastError());
-	    goto initFailure;
-	}
-
-	/*
-	 * Initialize the winsock library and check the interface version
-	 * actually loaded. We only ask for the 1.1 interface and do require
-	 * that it not be less than 1.1.
-	 */
+	Tcl_UtfToLower(Tcl_WinTCharToUtf(tbuf, -1, &ds));
 
-	err = WSAStartup((WORD) MAKEWORD(WSA_VERSION_MAJOR,WSA_VERSION_MINOR),
-		&wsaData);
-	if (err != 0) {
-	    TclWinConvertError(err);
-	    goto initFailure;
-	}
+    } else {
+	Tcl_DStringInit(&ds);
+	if (TclpHasSockets(NULL) == TCL_OK) {
+	    /*
+	     * The buffer size of 256 is recommended by the MSDN page that
+	     * documents gethostname() as being always adequate.
+	     */
 
-	/*
-	 * Note the byte positions ae swapped for the comparison, so that
-	 * 0x0002 (2.0, MAKEWORD(2,0)) doesn't look less than 0x0101 (1.1). We
-	 * want the comparison to be 0x0200 < 0x0101.
-	 */
+	    Tcl_DString inDs;
 
-	if (MAKEWORD(HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion))
-		< MAKEWORD(WSA_VERSION_MINOR, WSA_VERSION_MAJOR)) {
-	    TclWinConvertError(WSAVERNOTSUPPORTED);
-	    WSACleanup();
-	    goto initFailure;
+	    Tcl_DStringInit(&inDs);
+	    Tcl_DStringSetLength(&inDs, 256);
+	    if (gethostname(Tcl_DStringValue(&inDs),
+		    Tcl_DStringLength(&inDs)) == 0) {
+		Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&inDs), -1,
+			&ds);
+	    }
+	    Tcl_DStringFree(&inDs);
 	}
     }
 
-    /*
-     * Check for per-thread initialization.
-     */
-
-    if (tsdPtr != NULL) {
-	return;
-    }
-
-    /*
-     * OK, this thread has never done anything with sockets before.  Construct
-     * a worker thread to handle asynchronous events related to sockets
-     * assigned to _this_ thread.
-     */
-
-    tsdPtr = TCL_TSD_INIT(&dataKey);
-    tsdPtr->pendingSocketInfo = NULL;
-    tsdPtr->socketList = NULL;
-    tsdPtr->hwnd       = NULL;
-    tsdPtr->threadId   = Tcl_GetCurrentThread();
-    tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (tsdPtr->readyEvent == NULL) {
-	goto initFailure;
-    }
-    tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL);
-    if (tsdPtr->socketListLock == NULL) {
-	goto initFailure;
-    }
-    tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread, tsdPtr, 0,
-	    &id);
-    if (tsdPtr->socketThread == NULL) {
-	goto initFailure;
-    }
-
-    SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST);
-
-    /*
-     * Wait for the thread to signal when the window has been created and if
-     * it is ready to go.
-     */
-
-    WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
-
-    if (tsdPtr->hwnd == NULL) {
-	goto initFailure;	/* Trouble creating the window. */
-    }
-
-    Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
-    return;
-
-  initFailure:
-    TclpFinalizeSockets();
-    initialized = -1;
-    return;
+    *encodingPtr = Tcl_GetEncoding(NULL, "utf-8");
+    *lengthPtr = Tcl_DStringLength(&ds);
+    *valuePtr = ckalloc((*lengthPtr) + 1);
+    memcpy(*valuePtr, Tcl_DStringValue(&ds), (size_t)(*lengthPtr)+1);
+    Tcl_DStringFree(&ds);
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * SocketsEnabled --
+ * Tcl_GetHostName --
  *
- *	Check that the WinSock was successfully initialized.
+ *	Returns the name of the local host.
  *
  * Results:
- *	1 if it is.
+ *	A string containing the network name for this machine, or an empty
+ *	string if we can't figure out the name. The caller must not modify or
+ *	free this string.
  *
  * Side effects:
- *	None.
+ *	Caches the name to return for future calls.
  *
  *----------------------------------------------------------------------
  */
 
-    /* ARGSUSED */
-static int
-SocketsEnabled(void)
+const char *
+Tcl_GetHostName(void)
 {
-    int enabled;
-
-    Tcl_MutexLock(&socketMutex);
-    enabled = (initialized == 1);
-    Tcl_MutexUnlock(&socketMutex);
-    return enabled;
+    return Tcl_GetString(TclGetProcessGlobalValue(&hostName));
 }
-
 
 /*
  *----------------------------------------------------------------------
  *
- * SocketExitHandler --
+ * TclpHasSockets --
  *
- *	Callback invoked during exit clean up to delete the socket
- *	communication window and to release the WinSock DLL.
+ *	This function determines whether sockets are available on the current
+ *	system and returns an error in interp if they are not. Note that
+ *	interp may be NULL.
  *
  * Results:
- *	None.
+ *	Returns TCL_OK if the system supports sockets, or TCL_ERROR with an
+ *	error in interp (if non-NULL).
  *
  * Side effects:
- *	None.
+ *	If not already prepared, initializes the TSD structure and socket
+ *	message handling thread associated to the calling thread for the
+ *	subsystem of the driver.
  *
  *----------------------------------------------------------------------
  */
 
-    /* ARGSUSED */
-static void
-SocketExitHandler(
-    ClientData clientData)		/* Not used. */
+int
+TclpHasSockets(
+    Tcl_Interp *interp)		/* Where to write an error message if sockets
+				 * are not present, or NULL if no such message
+				 * is to be written. */
 {
     Tcl_MutexLock(&socketMutex);
-
-    /*
-     * Make sure the socket event handling window is cleaned-up for, at
-     * most, this thread.
-     */
-
-    TclpFinalizeSockets();
-    UnregisterClass(classname, TclWinGetTclInstance());
-    WSACleanup();
-    initialized = 0;
+    InitSockets();
     Tcl_MutexUnlock(&socketMutex);
+
+    if (SocketsEnabled()) {
+	return TCL_OK;
+    }
+    if (interp != NULL) {
+	Tcl_SetObjResult(interp, Tcl_NewStringObj(
+		"sockets are not available on this system", -1));
+    }
+    return TCL_ERROR;
 }
 
 /*
@@ -570,379 +519,400 @@ TclpFinalizeSockets(void)
 /*
  *----------------------------------------------------------------------
  *
- * TclpHasSockets --
+ * TcpBlockModeProc --
  *
- *	This function determines whether sockets are available on the current
- *	system and returns an error in interp if they are not. Note that
- *	interp may be NULL.
+ *	This function is invoked by the generic IO level to set blocking and
+ *	nonblocking mode on a TCP socket based channel.
  *
  * Results:
- *	Returns TCL_OK if the system supports sockets, or TCL_ERROR with an
- *	error in interp (if non-NULL).
+ *	0 if successful, errno when failed.
  *
  * Side effects:
- *	If not already prepared, initializes the TSD structure and socket
- *	message handling thread associated to the calling thread for the
- *	subsystem of the driver.
+ *	Sets the device into blocking or nonblocking mode.
  *
  *----------------------------------------------------------------------
  */
 
-int
-TclpHasSockets(
-    Tcl_Interp *interp)		/* Where to write an error message if sockets
-				 * are not present, or NULL if no such message
-				 * is to be written. */
+	/* ARGSUSED */
+static int
+TcpBlockModeProc(
+    ClientData instanceData,	/* Socket state. */
+    int mode)			/* The mode to set. Can be one of
+				 * TCL_MODE_BLOCKING or
+				 * TCL_MODE_NONBLOCKING. */
 {
-    Tcl_MutexLock(&socketMutex);
-    InitSockets();
-    Tcl_MutexUnlock(&socketMutex);
+    TcpState *statePtr = instanceData;
 
-    if (SocketsEnabled()) {
-	return TCL_OK;
-    }
-    if (interp != NULL) {
-	Tcl_SetObjResult(interp, Tcl_NewStringObj(
-		"sockets are not available on this system", -1));
+    if (mode == TCL_MODE_NONBLOCKING) {
+	statePtr->flags |= TCP_ASYNC_SOCKET;
+    } else {
+	statePtr->flags &= ~(TCP_ASYNC_SOCKET);
     }
-    return TCL_ERROR;
+    return 0;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * SocketSetupProc --
+ * WaitForConnect --
  *
- *	This function is invoked before Tcl_DoOneEvent blocks waiting for an
- *	event.
+ *	Process an asyncroneous connect by other commands (gets... ).
+ *	Do one connect step if pending as if the event loop would run.
+ *
+ *	Blocking commands may call in with terminate_connect to terminate
+ *	the syncroneous connect syncroneously.
+ *
+ *	Ok is directly returned if no async connect is running.
  *
  * Results:
- *	None.
+ *	Returns 1 on success or 0 on failure, with a possix error code in
+ *	errorCodePtr.
+ *	If the connect is not terminated, errorCode is set to EWOULDBLOCK
+ *	and 0 is returned.
  *
  * Side effects:
- *	Adjusts the block time if needed.
+ *	Processes socket events off the system queue.
+ *	May process asynchroneous connect.
  *
  *----------------------------------------------------------------------
  */
 
-void
-SocketSetupProc(
-    ClientData data,		/* Not used. */
-    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
+static int
+WaitForConnect(
+    TcpState *statePtr,		/* State of the socket. */
+    int *errorCodePtr,		/* Where to store errors? */
+    int terminate_connect)	/* Should the connect be terminated? */
 {
-    SocketInfo *infoPtr;
-    Tcl_Time blockTime = { 0, 0 };
-    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
-    if (!(flags & TCL_FILE_EVENTS)) {
-	return;
-    }
+    int result;
+    int oldMode;
+    ThreadSpecificData *tsdPtr;
 
     /*
-     * Check to see if there is a ready socket.	 If so, poll.
+     * Check if an async connect is running. If not return ok
+     */
+    if ( !(statePtr->flags & SOCKET_REENTER_PENDING) )
+	return 1;
+    
+    /*
+     * Be sure to disable event servicing so we are truly modal.
      */
-    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
-    for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
-	    infoPtr = infoPtr->nextPtr) {
-	if (infoPtr->readyEvents &
-	    (infoPtr->watchEvents | FD_CONNECT | FD_ACCEPT)
-	) {
-	    DEBUG("Tcl_SetMaxBlockTime");
-	    Tcl_SetMaxBlockTime(&blockTime);
-	    break;
-	}
-    }
-    SetEvent(tsdPtr->socketListLock);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * SocketCheckProc --
- *
- *	This function is called by Tcl_DoOneEvent to check the socket event
- *	source for events.
- *
- * Results:
- *	None.
- *
- * Side effects:
- *	May queue an event.
- *
- *----------------------------------------------------------------------
- */
 
-static void
-SocketCheckProc(
-    ClientData data,		/* Not used. */
-    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
-{
-    SocketInfo *infoPtr;
-    SocketEvent *evPtr;
-    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+    oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
 
-    if (!(flags & TCL_FILE_EVENTS)) {
-	return;
-    }
+    while (1) {
 
-    /*
-     * Queue events for any ready sockets that don't already have events
-     * queued (caused by persistent states that won't generate WinSock
-     * events).
-     */
+	/* get statePtr lock */
+        tsdPtr = TclThreadDataKeyGet(&dataKey);
+	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+	
+	/* Check for connect event */
+	if (statePtr->readyEvents & FD_CONNECT) {
 
-    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
-    for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
-	    infoPtr = infoPtr->nextPtr) {
-	DEBUG("Socket loop");
-	if ((infoPtr->readyEvents &
-		(infoPtr->watchEvents | FD_CONNECT | FD_ACCEPT))
-	    && !(infoPtr->flags & SOCKET_PENDING)
-	) {
-	    DEBUG("Event found");
-	    infoPtr->flags |= SOCKET_PENDING;
-	    evPtr = ckalloc(sizeof(SocketEvent));
-	    evPtr->header.proc = SocketEventProc;
-	    evPtr->socket = infoPtr->sockets->fd;
-	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
+	    /* Consume the connect event */
+	    statePtr->readyEvents &= ~(FD_CONNECT);
+
+	    /*
+	     * For blocking sockets disable async connect
+	     * as we continue now synchoneously
+	     */
+	    if ( terminate_connect ) {
+		statePtr->flags &= ~(TCP_ASYNC_CONNECT);
+	    }
+
+	    /* Free list lock */
+	    SetEvent(tsdPtr->socketListLock);
+
+	    /* continue connect */
+	    result = CreateClientSocket(NULL, statePtr);
+
+	    /* Restore event service mode */
+	    (void) Tcl_SetServiceMode(oldMode);
+
+	    /* Succesfully connected or async connect restarted */
+	    if (result == TCL_OK) {
+		if ( statePtr->flags & SOCKET_REENTER_PENDING ) {
+		    *errorCodePtr = EWOULDBLOCK;
+		    return 0;
+		}
+		return 1;
+	    }
+	    /* error case */
+	    *errorCodePtr = Tcl_GetErrno();
+	    return 0;
+	}
+
+        /* Free list lock */
+        SetEvent(tsdPtr->socketListLock);
+
+	/*
+	 * A non blocking socket waiting for an asyncronous connect
+	 * returns directly an error
+	 */
+	if ( ! terminate_connect ) {
+	    *errorCodePtr = EWOULDBLOCK;
+	    return 0;
 	}
+
+	/*
+	 * Wait until something happens.
+	 */
+
+	WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
     }
-    SetEvent(tsdPtr->socketListLock);
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * SocketEventProc --
+ * TcpInputProc --
  *
- *	This function is called by Tcl_ServiceEvent when a socket event
- *	reaches the front of the event queue. This function is responsible for
- *	notifying the generic channel code.
+ *	This function is invoked by the generic IO level to read input from a
+ *	TCP socket based channel.
  *
  * Results:
- *	Returns 1 if the event was handled, meaning it should be removed from
- *	the queue. Returns 0 if the event was not handled, meaning it should
- *	stay on the queue. The only time the event isn't handled is if the
- *	TCL_FILE_EVENTS flag bit isn't set.
+ *	The number of bytes read is returned or -1 on error. An output
+ *	argument contains the POSIX error code on error, or zero if no error
+ *	occurred.
  *
  * Side effects:
- *	Whatever the channel callback functions do.
+ *	Reads input from the input device of the channel.
  *
  *----------------------------------------------------------------------
  */
 
+	/* ARGSUSED */
 static int
-SocketEventProc(
-    Tcl_Event *evPtr,		/* Event to service. */
-    int flags)			/* Flags that indicate what events to handle,
-				 * such as TCL_FILE_EVENTS. */
+TcpInputProc(
+    ClientData instanceData,	/* Socket state. */
+    char *buf,			/* Where to store data read. */
+    int bufSize,		/* How much space is available in the
+				 * buffer? */
+    int *errorCodePtr)		/* Where to store error code. */
 {
-    SocketInfo *infoPtr = NULL; /* DEBUG */
-    SocketEvent *eventPtr = (SocketEvent *) evPtr;
-    int mask = 0, events;
-    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-    TcpFdList *fds;
-    SOCKET newSocket;
-    address addr;
-    int len;
+    TcpState *statePtr = instanceData;
+    int bytesRead;
+    DWORD error;
+    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
 
-    DEBUG("");
-    if (!(flags & TCL_FILE_EVENTS)) {
-	return 0;
-    }
+    *errorCodePtr = 0;
 
     /*
-     * Find the specified socket on the socket list.
+     * Check that WinSock is initialized; do not call it if not, to prevent
+     * system crashes. This can happen at exit time if the exit handler for
+     * WinSock ran before other exit handlers that want to use sockets.
      */
 
-    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
-    for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
-	    infoPtr = infoPtr->nextPtr) {
-	if (infoPtr->sockets->fd == eventPtr->socket) {
-	    break;
-	}
+    if (!SocketsEnabled()) {
+	*errorCodePtr = EFAULT;
+	return -1;
     }
 
     /*
-     * Discard events that have gone stale.
+     * First check to see if EOF was already detected, to prevent calling the
+     * socket stack after the first time EOF is detected.
      */
 
-    if (!infoPtr) {
-        SetEvent(tsdPtr->socketListLock);
-	return 1;
+    if (statePtr->flags & SOCKET_EOF) {
+	return 0;
     }
 
-    infoPtr->flags &= ~SOCKET_PENDING;
+    /*
+     * Check if there is an async connect running.
+     * For blocking sockets terminate connect, otherwise do one step.
+     * For a non blocking socket return EWOULDBLOCK if connect not terminated
+     */
 
-    /* Continue async connect if pending and ready */
-    if ( infoPtr->readyEvents & FD_CONNECT ) {
-	infoPtr->readyEvents &= ~(FD_CONNECT);
-	DEBUG("FD_CONNECT");
-        if ( infoPtr->flags & SOCKET_REENTER_PENDING ) {
-	    SetEvent(tsdPtr->socketListLock);
-	    CreateClientSocket(NULL, infoPtr);
-	    return 1;
-	}
+    if ( !WaitForConnect(statePtr, errorCodePtr,
+	    ! ( statePtr->flags & TCP_ASYNC_SOCKET ))) {
+	return -1;
     }
 
     /*
-     * Handle connection requests directly.
+     * No EOF, and it is connected, so try to read more from the socket. Note
+     * that we clear the FD_READ bit because read events are level triggered
+     * so a new event will be generated if there is still data available to be
+     * read. We have to simulate blocking behavior here since we are always
+     * using non-blocking sockets.
      */
-    if (infoPtr->readyEvents & FD_ACCEPT) {
-	for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
 
-	    /*
-	    * Accept the incoming connection request.
-	    */
-	    len = sizeof(address);
+    while (1) {
+	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+		(WPARAM) UNSELECT, (LPARAM) statePtr);
+	/* single fd operation: this proc is only called for a connected socket. */
+	bytesRead = recv(statePtr->sockets->fd, buf, bufSize, 0);
+	statePtr->readyEvents &= ~(FD_READ);
 
-	    newSocket = accept(fds->fd, &(addr.sa), &len);
+	/*
+	 * Check for end-of-file condition or successful read.
+	 */
 
-	    /* On Tcl server sockets with multiple OS fds we loop over the fds trying
-	     * an accept() on each, so we expect INVALID_SOCKET.  There are also other
-	     * network stack conditions that can result in FD_ACCEPT but a subsequent
-	     * failure on accept() by the time we get around to it.
-	     * Access to sockets (acceptEventCount, readyEvents) in socketList
-	     * is still protected by the lock (prevents reintroduction of 
-	     * SF Tcl Bug 3056775.
-	     */
+	if (bytesRead == 0) {
+	    statePtr->flags |= SOCKET_EOF;
+	}
+	if (bytesRead != SOCKET_ERROR) {
+	    break;
+	}
 
-	    if (newSocket == INVALID_SOCKET) {
-		/* int err = WSAGetLastError(); */
-		continue;
-	    }
-
-	    /*
-	     * It is possible that more than one FD_ACCEPT has been sent, so an extra
-	     * count must be kept. Decrement the count, and reset the readyEvent bit
-	     * if the count is no longer > 0.
-	     */
-	    infoPtr->acceptEventCount--;
-
-	    if (infoPtr->acceptEventCount <= 0) {
-		infoPtr->readyEvents &= ~(FD_ACCEPT);
-	    }
-
-	    SetEvent(tsdPtr->socketListLock);
-
-	    /* Caution: TcpAccept() has the side-effect of evaluating the server
-	     * accept script (via AcceptCallbackProc() in tclIOCmd.c), which can
-	     * close the server socket and invalidate infoPtr and fds.
-	     * If TcpAccept() accepts a socket we must return immediately and let
-	     * SocketCheckProc queue additional FD_ACCEPT events.
-	     */
-	    TcpAccept(fds, newSocket, addr);
-	    return 1;
-	}
-
-	/* Loop terminated with no sockets accepted; clear the ready mask so 
-	 * we can detect the next connection request. Note that connection 
-	 * requests are level triggered, so if there is a request already 
-	 * pending, a new event will be generated.
+	/*
+	 * If an error occurs after the FD_CLOSE has arrived, then ignore the
+	 * error and report an EOF.
 	 */
-	infoPtr->acceptEventCount = 0;
-	infoPtr->readyEvents &= ~(FD_ACCEPT);
-
-	SetEvent(tsdPtr->socketListLock);
-	return 1;
-    }
-
-    SetEvent(tsdPtr->socketListLock);
 
-    /*
-     * Mask off unwanted events and compute the read/write mask so we can
-     * notify the channel.
-     */
+	if (statePtr->readyEvents & FD_CLOSE) {
+	    statePtr->flags |= SOCKET_EOF;
+	    bytesRead = 0;
+	    break;
+	}
 
-    events = infoPtr->readyEvents & infoPtr->watchEvents;
+	error = WSAGetLastError();
 
-    if (events & FD_CLOSE) {
 	/*
-	 * If the socket was closed and the channel is still interested in
-	 * read events, then we need to ensure that we keep polling for this
-	 * event until someone does something with the channel. Note that we
-	 * do this before calling Tcl_NotifyChannel so we don't have to watch
-	 * out for the channel being deleted out from under us. This may cause
-	 * a redundant trip through the event loop, but it's simpler than
-	 * trying to do unwind protection.
+	 * If an RST comes, then ignore the error and report an EOF just like
+	 * on unix.
 	 */
 
-	Tcl_Time blockTime = { 0, 0 };
-
-	DEBUG("FD_CLOSE");
-	Tcl_SetMaxBlockTime(&blockTime);
-	mask |= TCL_READABLE|TCL_WRITABLE;
-    } else if (events & FD_READ) {
-	fd_set readFds;
-	struct timeval timeout;
+	if (error == WSAECONNRESET) {
+	    statePtr->flags |= SOCKET_EOF;
+	    bytesRead = 0;
+	    break;
+	}
 
 	/*
-	 * We must check to see if data is really available, since someone
-	 * could have consumed the data in the meantime. Turn off async
-	 * notification so select will work correctly. If the socket is still
-	 * readable, notify the channel driver, otherwise reset the async
-	 * select handler and keep waiting.
+	 * Check for error condition or underflow in non-blocking case.
 	 */
-	DEBUG("FD_READ");
 
-	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
-		(WPARAM) UNSELECT, (LPARAM) infoPtr);
+	if ((statePtr->flags & TCP_ASYNC_SOCKET) || (error != WSAEWOULDBLOCK)) {
+	    TclWinConvertError(error);
+	    *errorCodePtr = Tcl_GetErrno();
+	    bytesRead = -1;
+	    break;
+	}
 
-	FD_ZERO(&readFds);
-	FD_SET(infoPtr->sockets->fd, &readFds);
-	timeout.tv_usec = 0;
-	timeout.tv_sec = 0;
+	/*
+	 * In the blocking case, wait until the file becomes readable or
+	 * closed and try again.
+	 */
 
-	if (select(0, &readFds, NULL, NULL, &timeout) != 0) {
-	    mask |= TCL_READABLE;
-	} else {
-	    infoPtr->readyEvents &= ~(FD_READ);
-	    SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
-		    (WPARAM) SELECT, (LPARAM) infoPtr);
+	if (!WaitForSocketEvent(statePtr, FD_READ|FD_CLOSE, errorCodePtr)) {
+	    bytesRead = -1;
+	    break;
 	}
     }
-    if (events & FD_WRITE) {
-	DEBUG("FD_WRITE");
-	mask |= TCL_WRITABLE;
-    }
-    if (mask) {
-	DEBUG("Calling Tcl_NotifyChannel...");
-	Tcl_NotifyChannel(infoPtr->channel, mask);
-    }
-    DEBUG("returning...");
-    return 1;
+
+    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr);
+
+    return bytesRead;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TcpBlockProc --
+ * TcpOutputProc --
  *
- *	Sets a socket into blocking or non-blocking mode.
+ *	This function is called by the generic IO level to write data to a
+ *	socket based channel.
  *
  * Results:
- *	0 if successful, errno if there was an error.
+ *	The number of bytes written or -1 on failure.
  *
  * Side effects:
- *	None.
+ *	Produces output on the socket.
  *
  *----------------------------------------------------------------------
  */
 
 static int
-TcpBlockProc(
-    ClientData instanceData,	/* The socket to block/un-block. */
-    int mode)			/* TCL_MODE_BLOCKING or
-				 * TCL_MODE_NONBLOCKING. */
+TcpOutputProc(
+    ClientData instanceData,	/* Socket state. */
+    const char *buf,		/* The data buffer. */
+    int toWrite,		/* How many bytes to write? */
+    int *errorCodePtr)		/* Where to store error code. */
 {
-    SocketInfo *infoPtr = instanceData;
+    TcpState *statePtr = instanceData;
+    int written;
+    DWORD error;
+    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
 
-    if (mode == TCL_MODE_NONBLOCKING) {
-	infoPtr->flags |= TCP_ASYNC_SOCKET;
-    } else {
-	infoPtr->flags &= ~(TCP_ASYNC_SOCKET);
+    *errorCodePtr = 0;
+
+    /*
+     * Check that WinSock is initialized; do not call it if not, to prevent
+     * system crashes. This can happen at exit time if the exit handler for
+     * WinSock ran before other exit handlers that want to use sockets.
+     */
+
+    if (!SocketsEnabled()) {
+	*errorCodePtr = EFAULT;
+	return -1;
     }
-    return 0;
+
+    /*
+     * Check if there is an async connect running.
+     * For blocking sockets terminate connect, otherwise do one step.
+     * For a non blocking socket return EWOULDBLOCK if connect not terminated
+     */
+
+    if ( !WaitForConnect(statePtr, errorCodePtr,
+	    ! ( statePtr->flags & TCP_ASYNC_SOCKET ))) {
+	return -1;
+    }
+
+    while (1) {
+	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+		(WPARAM) UNSELECT, (LPARAM) statePtr);
+
+	/* single fd operation: this proc is only called for a connected socket. */
+	written = send(statePtr->sockets->fd, buf, toWrite, 0);
+	if (written != 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 (statePtr->watchEvents & FD_WRITE) {
+		Tcl_Time blockTime = { 0, 0 };
+		Tcl_SetMaxBlockTime(&blockTime);
+	    }
+	    break;
+	}
+
+	/*
+	 * Check for error condition or overflow. In the event of overflow, we
+	 * need to clear the FD_WRITE flag so we can detect the next writable
+	 * event. Note that Windows only sends a new writable event after a
+	 * send fails with WSAEWOULDBLOCK.
+	 */
+
+	error = WSAGetLastError();
+	if (error == WSAEWOULDBLOCK) {
+	    statePtr->readyEvents &= ~(FD_WRITE);
+	    if (statePtr->flags & TCP_ASYNC_SOCKET) {
+		*errorCodePtr = EWOULDBLOCK;
+		written = -1;
+		break;
+	    }
+	} else {
+	    TclWinConvertError(error);
+	    *errorCodePtr = Tcl_GetErrno();
+	    written = -1;
+	    break;
+	}
+
+	/*
+	 * In the blocking case, wait until the file becomes writable or
+	 * closed and try again.
+	 */
+
+	if (!WaitForSocketEvent(statePtr, FD_WRITE|FD_CLOSE, errorCodePtr)) {
+	    written = -1;
+	    break;
+	}
+    }
+
+    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr);
+
+    return written;
 }
 
 /*
@@ -969,7 +939,7 @@ TcpCloseProc(
     ClientData instanceData,	/* The socket to close. */
     Tcl_Interp *interp)		/* Unused. */
 {
-    SocketInfo *infoPtr = instanceData;
+    TcpState *statePtr = instanceData;
     /* TIP #218 */
     int errorCode = 0;
     /* ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); */
@@ -987,9 +957,9 @@ TcpCloseProc(
 	 * background.
 	 */
 
-	while ( infoPtr->sockets != NULL ) {
-	    TcpFdList *thisfd = infoPtr->sockets;
-	    infoPtr->sockets = thisfd->next;
+	while ( statePtr->sockets != NULL ) {
+	    TcpFdList *thisfd = statePtr->sockets;
+	    statePtr->sockets = thisfd->next;
 
 	    if (closesocket(thisfd->fd) == SOCKET_ERROR) {
 		TclWinConvertError((DWORD) WSAGetLastError());
@@ -999,11 +969,11 @@ TcpCloseProc(
 	}
     }
 
-    if (infoPtr->addrlist != NULL) {
-        freeaddrinfo(infoPtr->addrlist);
+    if (statePtr->addrlist != NULL) {
+        freeaddrinfo(statePtr->addrlist);
     }
-    if (infoPtr->myaddrlist != NULL) {
-        freeaddrinfo(infoPtr->myaddrlist);
+    if (statePtr->myaddrlist != NULL) {
+        freeaddrinfo(statePtr->myaddrlist);
     }
 
     /*
@@ -1013,7 +983,7 @@ TcpCloseProc(
      * fear of damaging the list.
      */
 
-    ckfree(infoPtr);
+    ckfree(statePtr);
     return errorCode;
 }
 
@@ -1040,14 +1010,15 @@ TcpClose2Proc(
     Tcl_Interp *interp,		/* For error reporting. */
     int flags)			/* Flags that indicate which side to close. */
 {
-    SocketInfo *infoPtr = instanceData;
-    int errorCode = 0, sd;
+    TcpState *statePtr = instanceData;
+    int errorCode = 0;
+    int sd;
 
     /*
      * Shutdown the OS socket handle.
      */
 
-    switch (flags) {
+    switch(flags) {
     case TCL_CLOSE_READ:
 	sd = SD_RECEIVE;
 	break;
@@ -1057,14 +1028,14 @@ TcpClose2Proc(
     default:
 	if (interp) {
 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
-		    "Socket close2proc called bidirectionally", -1));
+		    "socket close2proc called bidirectionally", -1));
 	}
 	return TCL_ERROR;
     }
 
     /* single fd operation: Tcl_OpenTcpServer() does not set TCL_READABLE or
      * TCL_WRITABLE so this should never be called for a server socket. */
-    if (shutdown(infoPtr->sockets->fd, sd) == SOCKET_ERROR) {
+    if (shutdown(statePtr->sockets->fd, sd) == SOCKET_ERROR) {
 	TclWinConvertError((DWORD) WSAGetLastError());
 	errorCode = Tcl_GetErrno();
     }
@@ -1075,745 +1046,799 @@ TcpClose2Proc(
 /*
  *----------------------------------------------------------------------
  *
- * AddSocketInfoFd --
+ * TcpSetOptionProc --
  *
- *	This function adds a SOCKET file descriptor to the 'sockets' linked 
- *	list of a SocketInfo structure.
+ *	Sets Tcp channel specific options.
  *
  * Results:
- *	None.
+ *	None, unless an error happens.
  *
  * Side effects:
- *	None, except for allocation of memory.
+ *	Changes attributes of the socket at the system level.
  *
  *----------------------------------------------------------------------
  */
 
-static void
-AddSocketInfoFd(
-    SocketInfo *infoPtr, 
-    SOCKET socket)
+static int
+TcpSetOptionProc(
+    ClientData instanceData,	/* Socket state. */
+    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
+    const char *optionName,	/* Name of the option to set. */
+    const char *value)		/* New value for option. */
 {
-    TcpFdList *fds = infoPtr->sockets;
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+    TcpState *statePtr = instanceData;
+    SOCKET sock;
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
 
-    if ( fds == NULL ) {
-	/* Add the first FD */
-	infoPtr->sockets = ckalloc(sizeof(TcpFdList));
-	fds = infoPtr->sockets;
-    } else {
-	/* Find end of list and append FD */
-	while ( fds->next != NULL ) {
-	    fds = fds->next;
+    /*
+     * Check that WinSock is initialized; do not call it if not, to prevent
+     * system crashes. This can happen at exit time if the exit handler for
+     * WinSock ran before other exit handlers that want to use sockets.
+     */
+
+    if (!SocketsEnabled()) {
+	if (interp) {
+	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
+		    "winsock is not initialized", -1));
 	}
-    
-	fds->next = ckalloc(sizeof(TcpFdList));
-	fds = fds->next;
+	return TCL_ERROR;
     }
 
-    /* Populate new FD */
-    fds->fd = socket;
-    fds->infoPtr = infoPtr;
-    fds->next = NULL;
-}
-
-    
-/*
- *----------------------------------------------------------------------
- *
- * NewSocketInfo --
- *
- *	This function allocates and initializes a new SocketInfo structure.
- *
- * Results:
- *	Returns a newly allocated SocketInfo.
- *
- * Side effects:
- *	None, except for allocation of memory.
- *
- *----------------------------------------------------------------------
- */
-
-static SocketInfo *
-NewSocketInfo(SOCKET socket)
-{
-    SocketInfo *infoPtr = ckalloc(sizeof(SocketInfo));
-
-    memset(infoPtr, 0, sizeof(SocketInfo));
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+    #error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat statePtr->sockets as single fd or list"
+    sock = statePtr->sockets->fd;
 
-    /*
-     * TIP #218. Removed the code inserting the new structure into the global
-     * list. This is now handled in the thread action callbacks, and only
-     * there.
-     */
+    if (!strcasecmp(optionName, "-keepalive")) {
+	BOOL val = FALSE;
+	int boolVar, rtn;
 
-    AddSocketInfoFd(infoPtr, socket);
+	if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (boolVar) {
+	    val = TRUE;
+	}
+	rtn = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
+		(const char *) &val, sizeof(BOOL));
+	if (rtn != 0) {
+	    TclWinConvertError(WSAGetLastError());
+	    if (interp) {
+		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+			"couldn't set socket option: %s",
+			Tcl_PosixError(interp)));
+	    }
+	    return TCL_ERROR;
+	}
+	return TCL_OK;
+    } else if (!strcasecmp(optionName, "-nagle")) {
+	BOOL val = FALSE;
+	int boolVar, rtn;
+
+	if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (!boolVar) {
+	    val = TRUE;
+	}
+	rtn = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+		(const char *) &val, sizeof(BOOL));
+	if (rtn != 0) {
+	    TclWinConvertError(WSAGetLastError());
+	    if (interp) {
+		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+			"couldn't set socket option: %s",
+			Tcl_PosixError(interp)));
+	    }
+	    return TCL_ERROR;
+	}
+	return TCL_OK;
+    }
 
-    return infoPtr;
+    return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
+#else
+    return Tcl_BadChannelOption(interp, optionName, "");
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * CreateClientSocket --
+ * TcpGetOptionProc --
  *
- *	This function opens a new socket in client mode.
+ *	Computes an option value for a TCP socket based channel, or a list of
+ *	all options and their values.
  *
- *	This might be called in 3 circumstances:
- *	-   By a regular socket command
- *	-   By the event handler to continue an asynchroneous connect
- *	-   By a blocking socket function (gets/puts) to terminate the
- *	    connect synchroneously
+ *	Note: This code is based on code contributed by John Haxby.
  *
  * Results:
- *      TCL_OK, if the socket was successfully connected or an asynchronous
- *      connection is in progress. If an error occurs, TCL_ERROR is returned
- *      and an error message is left in interp.
+ *	A standard Tcl result. The value of the specified option or a list of
+ *	all options and their values is returned in the supplied DString. Sets
+ *	Error message if needed.
  *
  * Side effects:
- *	Opens a socket.
- *
- * Remarks:
- *	A single host name may resolve to more than one IP address, e.g. for
- *	an IPv4/IPv6 dual stack host. For handling asyncronously connecting
- *	sockets in the background for such hosts, this function can act as a
- *	coroutine. On the first call, it sets up the control variables for the
- *	two nested loops over the local and remote addresses. Once the first
- *	connection attempt is in progress, it sets up itself as a writable
- *	event handler for that socket, and returns. When the callback occurs,
- *	control is transferred to the "reenter" label, right after the initial
- *	return and the loops resume as if they had never been interrupted.
- *	For syncronously connecting sockets, the loops work the usual way.
+ *	None.
  *
  *----------------------------------------------------------------------
  */
 
 static int
-CreateClientSocket(
-    Tcl_Interp *interp,		/* For error reporting; can be NULL. */
-    SocketInfo *infoPtr)
+TcpGetOptionProc(
+    ClientData instanceData,	/* Socket state. */
+    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
+    const char *optionName,	/* Name of the option to retrieve the value
+				 * for, or NULL to get all options and their
+				 * values. */
+    Tcl_DString *dsPtr)		/* Where to store the computed value;
+				 * initialized by caller. */
 {
-    DWORD error;
-    u_long flag = 1;		/* Indicates nonblocking mode. */
+    TcpState *statePtr = instanceData;
+    char host[NI_MAXHOST], port[NI_MAXSERV];
+    SOCKET sock;
+    size_t len = 0;
+    int reverseDNS = 0;
+#define SUPPRESS_RDNS_VAR "::tcl::unsupported::noReverseDNS"
+
     /*
-     * We are started with async connect and the connect notification
-     * was not jet received
+     * Check that WinSock is initialized; do not call it if not, to prevent
+     * system crashes. This can happen at exit time if the exit handler for
+     * WinSock ran before other exit handlers that want to use sockets.
      */
-    int async_connect = infoPtr->flags & SOCKET_ASYNC_CONNECT;
-    /* We were called by the event procedure and continue our loop */
-    int async_callback = infoPtr->sockets->fd != INVALID_SOCKET;
-    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
-    
-    DEBUG(async_connect ? "async connect" : "sync connect");
 
-    if (async_callback) {
-	DEBUG("subsequent call");
-        goto reenter;
-    } else {
-	DEBUG("first call");
+    if (!SocketsEnabled()) {
+	if (interp) {
+	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
+		    "winsock is not initialized", -1));
+	}
+	return TCL_ERROR;
     }
-    
-    for (infoPtr->addr = infoPtr->addrlist; infoPtr->addr != NULL;
-	 infoPtr->addr = infoPtr->addr->ai_next) {
-	
-        for (infoPtr->myaddr = infoPtr->myaddrlist; infoPtr->myaddr != NULL;
-	     infoPtr->myaddr = infoPtr->myaddr->ai_next) {
-
-	    DEBUG("inner loop");
-
-	    /*
-	     * No need to try combinations of local and remote addresses
-	     * of different families.
-	     */
-
-	    if (infoPtr->myaddr->ai_family != infoPtr->addr->ai_family) {
-		DEBUG("family mismatch");
-		continue;
-	    }
 
-	    DEBUG(infoPtr->myaddr->ai_family == AF_INET ? "IPv4" : "IPv6");
-	    printaddrinfo(infoPtr->myaddr, "~~ from");
-	    printaddrinfo(infoPtr->addr,   "~~   to");
+    sock = statePtr->sockets->fd;
+    if (optionName != NULL) {
+	len = strlen(optionName);
+    }
 
-            /*
-             * Close the socket if it is still open from the last unsuccessful
-             * iteration.
-             */	    
-	    if (infoPtr->sockets->fd != INVALID_SOCKET) {
-		DEBUG("closesocket");
-		closesocket(infoPtr->sockets->fd);
-	    }
+    if ((len > 1) && (optionName[1] == 'e') &&
+	    (strncmp(optionName, "-error", len) == 0)) {
 
-	    /* get infoPtr lock */
-	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+	if ( (statePtr->flags & SOCKET_REENTER_PENDING) ) {
 
 	    /*
-	     * Reset last error from last try
+	     * Asyncroneous connect is running.
+	     * Process it one step without blocking.
+	     * Return its error or nothing if connect not
+	     * terminated.
 	     */
-	    infoPtr->lastError = 0;
-	    Tcl_SetErrno(0);
-	    
-	    infoPtr->sockets->fd = socket(infoPtr->myaddr->ai_family, SOCK_STREAM, 0);
-	    
-	    /* Free list lock */
-	    SetEvent(tsdPtr->socketListLock);
 
-	    /* continue on socket creation error */
-	    if (infoPtr->sockets->fd == INVALID_SOCKET) {
-		DEBUG("socket() failed");
-		TclWinConvertError((DWORD) WSAGetLastError());
-		continue;
+	    int errorCode;
+	    if (!WaitForConnect(statePtr, &errorCode, 0)
+		    && errorCode != EWOULDBLOCK) {
+		/* connect terminated with error */
+		Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errorCode), -1);
 	    }
-	    
-#ifdef DEBUGGING
-	    fprintf(stderr, "Client socket %d created\n", infoPtr->sockets->fd);
-#endif
-	    /*
-	     * Win-NT has a misfeature that sockets are inherited in child
-	     * processes by default. Turn off the inherit bit.
-	     */
 
-	    SetHandleInformation((HANDLE) infoPtr->sockets->fd, HANDLE_FLAG_INHERIT, 0);
+	} else {
+	    int optlen;
+	    int ret;
+    	    DWORD err;
 
 	    /*
-	     * Set kernel space buffering
+	     * Populater the err Variable with a possix error
 	     */
-
-	    TclSockMinimumBuffers((void *) infoPtr->sockets->fd, TCP_BUFFER_SIZE);
-
+	    optlen = sizeof(int);
+	    ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
+		    (char *)&err, &optlen);
 	    /*
-	     * Try to bind to a local port.
+	     * The error was not returned directly but should be
+	     * taken from WSA
 	     */
-
-	    if (bind(infoPtr->sockets->fd, infoPtr->myaddr->ai_addr,
-			infoPtr->myaddr->ai_addrlen) == SOCKET_ERROR) {
-		DEBUG("bind() failed");
-		TclWinConvertError((DWORD) WSAGetLastError());
-		continue;
+	    if (ret == SOCKET_ERROR) {
+		err = WSAGetLastError();
 	    }
 	    /*
-	     * For asyncroneous connect set the socket in nonblocking mode
-	     * and activate connect notification
+	     * Return error message
 	     */
-	    if (async_connect) {
-		SocketInfo *infoPtr2;
-		int in_socket_list = 0;
-		/* get infoPtr lock */
-		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+	    if (err) {
+		TclWinConvertError(err);
+		Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
+	    }
+	}
+	return TCL_OK;
+    }
 
-		/*
-		 * Check if my infoPtr is already in the tsdPtr->socketList
-		 * It is set after this call by TcpThreadActionProc and is set
-		 * on a second round.
-		 *
-		 * If not, we buffer my infoPtr in the tsd memory so it is not
-		 * lost by the event procedure
-		 */
+    if (interp != NULL && Tcl_GetVar(interp, SUPPRESS_RDNS_VAR, 0) != NULL) {
+	reverseDNS = NI_NUMERICHOST;
+    }
 
-		for (infoPtr2 = tsdPtr->socketList; infoPtr2 != NULL;
-			infoPtr2 = infoPtr->nextPtr) {
-		    if (infoPtr2 == infoPtr) {
-			in_socket_list = 1;
-			break;
-		    }
-		}
-		if (!in_socket_list) {
-		    tsdPtr->pendingSocketInfo = infoPtr;
-		}
-		/*
-		 * Set connect mask to connect events
-		 * This is activated by a SOCKET_SELECT message to the notifier
-		 * thread.
-		 */
-		infoPtr->selectEvents |= FD_CONNECT;
-		
-		/*
-		 * Free list lock
-		 */
-		SetEvent(tsdPtr->socketListLock);
+    if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
+	    (strncmp(optionName, "-peername", len) == 0))) {
+	address peername;
+	socklen_t size = sizeof(peername);
 
-    		/* activate accept notification */
-		SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
-			(LPARAM) infoPtr);
+	if (getpeername(sock, (LPSOCKADDR) &(peername.sa), &size) == 0) {
+	    if (len == 0) {
+		Tcl_DStringAppendElement(dsPtr, "-peername");
+		Tcl_DStringStartSublist(dsPtr);
 	    }
 
+	    getnameinfo(&(peername.sa), size, host, sizeof(host),
+		    NULL, 0, NI_NUMERICHOST);
+	    Tcl_DStringAppendElement(dsPtr, host);
+	    getnameinfo(&(peername.sa), size, host, sizeof(host),
+		    port, sizeof(port), reverseDNS | NI_NUMERICSERV);
+	    Tcl_DStringAppendElement(dsPtr, host);
+	    Tcl_DStringAppendElement(dsPtr, port);
+	    if (len == 0) {
+		Tcl_DStringEndSublist(dsPtr);
+	    } else {
+		return TCL_OK;
+	    }
+	} else {
 	    /*
-	     * Attempt to connect to the remote socket.
+	     * getpeername failed - but if we were asked for all the options
+	     * (len==0), don't flag an error at that point because it could be
+	     * an fconfigure request on a server socket (such sockets have no
+	     * peer). {Copied from unix/tclUnixChan.c}
 	     */
-	    
-	    DEBUG("connect()");
-	    connect(infoPtr->sockets->fd, infoPtr->addr->ai_addr,
-		    infoPtr->addr->ai_addrlen);
-
-	    error = WSAGetLastError();
-	    TclWinConvertError(error);
 
-	    if (async_connect && error == WSAEWOULDBLOCK) {
-		/*
-		 * Asynchroneous connect
-		 */
-		DEBUG("WSAEWOULDBLOCK");
+	    if (len) {
+		TclWinConvertError((DWORD) WSAGetLastError());
+		if (interp) {
+		    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+			    "can't get peername: %s",
+			    Tcl_PosixError(interp)));
+		}
+		return TCL_ERROR;
+	    }
+	}
+    }
 
+    if ((len == 0) || ((len > 1) && (optionName[1] == 's') &&
+	    (strncmp(optionName, "-sockname", len) == 0))) {
+	TcpFdList *fds;
+	address sockname;
+	socklen_t size;
+	int found = 0;
 
-		/*
-		 * Remember that we jump back behind this next round
-		 */
-		infoPtr->flags |= SOCKET_REENTER_PENDING;
-		return TCL_OK;
+	if (len == 0) {
+	    Tcl_DStringAppendElement(dsPtr, "-sockname");
+	    Tcl_DStringStartSublist(dsPtr);
+	}
+	for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
+	    sock = fds->fd;
+#ifdef DEBUGGING
+	    fprintf(stderr, "sock == %d\n", sock);
+#endif
+	    size = sizeof(sockname);
+	    if (getsockname(sock, &(sockname.sa), &size) >= 0) {
+		int flags = reverseDNS;
+
+		found = 1;
+		getnameinfo(&sockname.sa, size, host, sizeof(host),
+			NULL, 0, NI_NUMERICHOST);
+		Tcl_DStringAppendElement(dsPtr, host);
 
-	    reenter:
-		DEBUG("reenter");
 		/*
-		 * Re-entry point for async connect after connect event or
-		 * blocking operation
-		 *
-		 * Clear the reenter flag
+		 * We don't want to resolve INADDR_ANY and sin6addr_any; they
+		 * can sometimes cause problems (and never have a name).
 		 */
-		infoPtr->flags &= ~(SOCKET_REENTER_PENDING);
-		/* get infoPtr lock */
-		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
-		/* Get signaled connect error */
-		Tcl_SetErrno(infoPtr->lastError);
-		/* Clear eventual connect flag */
-		infoPtr->selectEvents &= ~(FD_CONNECT);
-		/* Free list lock */
-		SetEvent(tsdPtr->socketListLock);
+		flags |= NI_NUMERICSERV;
+		if (sockname.sa.sa_family == AF_INET) {
+		    if (sockname.sa4.sin_addr.s_addr == INADDR_ANY) {
+			flags |= NI_NUMERICHOST;
+		    }
+		} else if (sockname.sa.sa_family == AF_INET6) {
+		    if ((IN6_ARE_ADDR_EQUAL(&sockname.sa6.sin6_addr,
+				&in6addr_any)) ||
+			    (IN6_IS_ADDR_V4MAPPED(&sockname.sa6.sin6_addr)
+			    && sockname.sa6.sin6_addr.s6_addr[12] == 0
+			    && sockname.sa6.sin6_addr.s6_addr[13] == 0
+			    && sockname.sa6.sin6_addr.s6_addr[14] == 0
+			    && sockname.sa6.sin6_addr.s6_addr[15] == 0)) {
+			flags |= NI_NUMERICHOST;
+		    }
+		}
+		getnameinfo(&sockname.sa, size, host, sizeof(host),
+			port, sizeof(port), flags);
+		Tcl_DStringAppendElement(dsPtr, host);
+		Tcl_DStringAppendElement(dsPtr, port);
 	    }
-#ifdef DEBUGGING
-	    fprintf(stderr, "lastError: %d\n", Tcl_GetErrno());
-#endif
-	    /*
-	     * Clear the tsd socket list pointer if we did not wait for
-	     * the FD_CONNECT asyncroneously
-	     */
-	    tsdPtr->pendingSocketInfo = NULL;
-	    
-	    if (Tcl_GetErrno() == 0) {
-		goto out;
+	}
+	if (found) {
+	    if (len == 0) {
+		Tcl_DStringEndSublist(dsPtr);
+	    } else {
+		return TCL_OK;
 	    }
+	} else {
+	    if (interp) {
+		TclWinConvertError((DWORD) WSAGetLastError());
+		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+			"can't get sockname: %s", Tcl_PosixError(interp)));
+	    }
+	    return TCL_ERROR;
 	}
     }
 
-out:
-    /*
-     * Socket connected or connection failed
-     */
-    DEBUG("connected or finally failed");
-    /* Clear async flag (not really necessary, not used any more) */
-    infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+    if (len == 0 || !strncmp(optionName, "-keepalive", len)) {
+	int optlen;
+	BOOL opt = FALSE;
 
-    /*
-     * Final connect failure
-     */
+	if (len == 0) {
+	    Tcl_DStringAppendElement(dsPtr, "-keepalive");
+	}
+	optlen = sizeof(BOOL);
+	getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, &optlen);
+	if (opt) {
+	    Tcl_DStringAppendElement(dsPtr, "1");
+	} else {
+	    Tcl_DStringAppendElement(dsPtr, "0");
+	}
+	if (len > 0) {
+	    return TCL_OK;
+	}
+    }
 
-    if ( Tcl_GetErrno() == 0 ) {
-	/*
-	 * Succesfully connected
-	 */
-	/*
-	 * Set up the select mask for read/write events.
-	 */
-	DEBUG("selectEvents = FD_READ | FD_WRITE | FD_CLOSE");    
-	infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
-        
-	/*
-	 * Register for interest in events in the select mask. Note that this
-	 * automatically places the socket into non-blocking mode.
-	 */
-        
-	SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
-		    (LPARAM) infoPtr);
-    } else {
-	/*
-	 * Connect failed
-	 */
-	DEBUG("ERRNO");
+    if (len == 0 || !strncmp(optionName, "-nagle", len)) {
+	int optlen;
+	BOOL opt = FALSE;
 
-	/*
-	 * For async connect schedule a writable event to report the fail.
-	 */
-	if (async_callback) {
-	    /*
-	     * Set up the select mask for read/write events.
-	     */
-	    DEBUG("selectEvents = FD_WRITE for fail writable");    
-	    infoPtr->selectEvents = FD_WRITE;
-	    /* get infoPtr lock */
-	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
-	    /* Clear eventual connect flag */
-	    infoPtr->readyEvents |= FD_WRITE;
-	    /* Free list lock */
-	    SetEvent(tsdPtr->socketListLock);
+	if (len == 0) {
+	    Tcl_DStringAppendElement(dsPtr, "-nagle");
 	}
-	/*
-	 * Error message on syncroneous connect
-	 */
-	if (interp != NULL) {
-	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
-		    "couldn't open socket: %s", Tcl_PosixError(interp)));
+	optlen = sizeof(BOOL);
+	getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, &optlen);
+	if (opt) {
+	    Tcl_DStringAppendElement(dsPtr, "0");
+	} else {
+	    Tcl_DStringAppendElement(dsPtr, "1");
 	}
-	return TCL_ERROR;
+	if (len > 0) {
+	    return TCL_OK;
+	}
+    }
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+
+    if (len > 0) {
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+	return Tcl_BadChannelOption(interp, optionName,
+		"peername sockname keepalive nagle");
+#else
+	return Tcl_BadChannelOption(interp, optionName, "peername sockname");
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
     }
+
     return TCL_OK;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * WaitForConnect --
- *
- *	Process an asyncroneous connect by other commands (gets... ).
- *	Do one connect step if pending as if the event loop would run.
- *
- *	Blocking commands may call in with terminate_connect to terminate
- *	the syncroneous connect syncroneously.
+ * TcpWatchProc --
  *
- *	This routine should only be called if flag SOCKET_REENTER_PENDING
- *	is set.
+ *	Informs the channel driver of the events that the generic channel code
+ *	wishes to receive on this socket.
  *
  * Results:
- *	Returns 1 on success or 0 on failure, with a possix error code in
- *	errorCodePtr.
- *	If the connect is not terminated, errorCode is set to EWOULDBLOCK
- *	and 0 is returned.
+ *	None.
  *
  * Side effects:
- *	Processes socket events off the system queue.
- *	May process asynchroneous connect.
+ *	May cause the notifier to poll if any of the specified conditions are
+ *	already true.
  *
  *----------------------------------------------------------------------
  */
 
-static int
-WaitForConnect(
-    SocketInfo *infoPtr,	/* Information about this socket. */
-    int *errorCodePtr,		/* Where to store errors? */
-    int terminate_connect)	/* Should the connect be terminated? */
+static void
+TcpWatchProc(
+    ClientData instanceData,	/* The socket state. */
+    int mask)			/* Events of interest; an OR-ed combination of
+				 * TCL_READABLE, TCL_WRITABLE and
+				 * TCL_EXCEPTION. */
 {
-    int result;
-    int oldMode;
-    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+    TcpState *statePtr = instanceData;
+
+    DEBUG((mask & TCL_READABLE) ? "+r":"-r");
+    DEBUG((mask & TCL_WRITABLE) ? "+w":"-w");
 
     /*
-     * Be sure to disable event servicing so we are truly modal.
+     * Update the watch events mask. Only if the socket is not a server
+     * socket. [Bug 557878]
      */
 
-    oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
-
-    while (1) {
-	
-	/* get infoPtr lock */
-        WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
-	
-	/* Check for connect event */
-	if (infoPtr->readyEvents & FD_CONNECT) {
-
-	    /* Consume the connect event */
-	    infoPtr->readyEvents &= ~(FD_CONNECT);
-
-	    /*
-	     * For blocking sockets disable async connect
-	     * as we continue now synchoneously
-	     */
-	    if ( terminate_connect ) {
-		infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
-	    }
-
-            /* Free list lock */
-            SetEvent(tsdPtr->socketListLock);
+    if (!statePtr->acceptProc) {
+	statePtr->watchEvents = 0;
+	if (mask & TCL_READABLE) {
+	    statePtr->watchEvents |= (FD_READ|FD_CLOSE);
+	}
+	if (mask & TCL_WRITABLE) {
+	    statePtr->watchEvents |= (FD_WRITE|FD_CLOSE);
+	}
 
-	    /* continue connect */
-	    result = CreateClientSocket(NULL, infoPtr);
+	/*
+	 * If there are any conditions already set, then tell the notifier to
+	 * poll rather than block.
+	 */
 
-	    /* Restore event service mode */
-	    (void) Tcl_SetServiceMode(oldMode);
+	if (statePtr->readyEvents & statePtr->watchEvents) {
+	    Tcl_Time blockTime = { 0, 0 };
 
-	    /* Succesfully connected or async connect restarted */
-	    if (result == TCL_OK) {
-		if ( infoPtr->flags & SOCKET_REENTER_PENDING ) {
-		    *errorCodePtr = EWOULDBLOCK;
-		    return 0;
-		}
-		return 1;
-	    }
-	    /* error case */
-	    *errorCodePtr = Tcl_GetErrno();
-	    return 0;
-	}
-
-        /* Free list lock */
-        SetEvent(tsdPtr->socketListLock);
-
-	/*
-	 * A non blocking socket waiting for an asyncronous connect
-	 * returns directly an error
-	 */
-	if ( ! terminate_connect ) {
-	    *errorCodePtr = EWOULDBLOCK;
-	    return 0;
+	    Tcl_SetMaxBlockTime(&blockTime);
 	}
-
-	/*
-	 * Wait until something happens.
-	 */
-
-	WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
     }
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * WaitForSocketEvent --
+ * TcpGetHandleProc --
  *
- *	Waits until one of the specified events occurs on a socket.
+ *	Called from Tcl_GetChannelHandle to retrieve OS handles from inside a
+ *	TCP socket based channel.
  *
  * Results:
- *	Returns 1 on success or 0 on failure, with an error code in
- *	errorCodePtr.
+ *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
+ *	handle for the specified direction.
  *
  * Side effects:
- *	Processes socket events off the system queue.
+ *	None.
  *
  *----------------------------------------------------------------------
  */
 
+	/* ARGSUSED */
 static int
-WaitForSocketEvent(
-    SocketInfo *infoPtr,	/* Information about this socket. */
-    int events,			/* Events to look for. */
-    int *errorCodePtr)		/* Where to store errors? */
+TcpGetHandleProc(
+    ClientData instanceData,	/* The socket state. */
+    int direction,		/* Not used. */
+    ClientData *handlePtr)	/* Where to store the handle. */
 {
-    int result = 1;
-    int oldMode;
-    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
-    /*
-     * Be sure to disable event servicing so we are truly modal.
-     */
-    DEBUG("=============");
-
-    oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
-
-    /*
-     * Reset WSAAsyncSelect so we have a fresh set of events pending.
-     */
-
-    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) UNSELECT,
-	    (LPARAM) infoPtr);
-    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
-	    (LPARAM) infoPtr);
-
-    while (1) {
-	if (infoPtr->lastError) {
-	    *errorCodePtr = infoPtr->lastError;
-	    result = 0;
-	    break;
-	} else if (infoPtr->readyEvents & events) {
-	    break;
-	} else if (infoPtr->flags & TCP_ASYNC_SOCKET) {
-	    *errorCodePtr = EWOULDBLOCK;
-	    result = 0;
-	    break;
-	}
+    TcpState *statePtr = instanceData;
 
-	/*
-	 * Wait until something happens.
-	 */
+    *handlePtr = INT2PTR(statePtr->sockets->fd);
+    return TCL_OK;
+}
 
-	WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
-    }
 
-    (void) Tcl_SetServiceMode(oldMode);
-    return result;
-}
 
 /*
  *----------------------------------------------------------------------
  *
- * Tcl_OpenTcpClient --
+ * CreateClientSocket --
  *
- *	Opens a TCP client socket and creates a channel around it.
+ *	This function opens a new socket in client mode.
+ *
+ *	This might be called in 3 circumstances:
+ *	-   By a regular socket command
+ *	-   By the event handler to continue an asynchroneous connect
+ *	-   By a blocking socket function (gets/puts) to terminate the
+ *	    connect synchroneously
  *
  * Results:
- *	The channel or NULL if failed. An error message is returned in the
- *	interpreter on failure.
+ *      TCL_OK, if the socket was successfully connected or an asynchronous
+ *      connection is in progress. If an error occurs, TCL_ERROR is returned
+ *      and an error message is left in interp.
  *
  * Side effects:
- *	Opens a client socket and creates a new channel.
+ *	Opens a socket.
+ *
+ * Remarks:
+ *	A single host name may resolve to more than one IP address, e.g. for
+ *	an IPv4/IPv6 dual stack host. For handling asyncronously connecting
+ *	sockets in the background for such hosts, this function can act as a
+ *	coroutine. On the first call, it sets up the control variables for the
+ *	two nested loops over the local and remote addresses. Once the first
+ *	connection attempt is in progress, it sets up itself as a writable
+ *	event handler for that socket, and returns. When the callback occurs,
+ *	control is transferred to the "reenter" label, right after the initial
+ *	return and the loops resume as if they had never been interrupted.
+ *	For syncronously connecting sockets, the loops work the usual way.
  *
  *----------------------------------------------------------------------
  */
 
-Tcl_Channel
-Tcl_OpenTcpClient(
+static int
+CreateClientSocket(
     Tcl_Interp *interp,		/* For error reporting; can be NULL. */
-    int port,			/* Port number to open. */
-    const char *host,		/* Host on which to open port. */
-    const char *myaddr,		/* Client-side address */
-    int myport,			/* Client-side port */
-    int async)			/* If nonzero, should connect client socket
-				 * asynchronously. */
+    TcpState *statePtr)
 {
-    SocketInfo *infoPtr;
-    const char *errorMsg = NULL;
-    struct addrinfo *addrlist = NULL;
-    struct addrinfo *myaddrlist = NULL;
-    char channelName[SOCK_CHAN_LENGTH];
-
-    if (TclpHasSockets(interp) != TCL_OK) {
-	return NULL;
-    }
-
+    DWORD error;
+    u_long flag = 1;		/* Indicates nonblocking mode. */
     /*
-     * Check that WinSock is initialized; do not call it if not, to prevent
-     * system crashes. This can happen at exit time if the exit handler for
-     * WinSock ran before other exit handlers that want to use sockets.
+     * We are started with async connect and the connect notification
+     * was not jet received
      */
+    int async_connect = statePtr->flags & TCP_ASYNC_CONNECT;
+    /* We were called by the event procedure and continue our loop */
+    int async_callback = statePtr->sockets->fd != INVALID_SOCKET;
+    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+    
+    DEBUG(async_connect ? "async connect" : "sync connect");
 
-    if (!SocketsEnabled()) {
-	return NULL;
+    if (async_callback) {
+	DEBUG("subsequent call");
+        goto reenter;
+    } else {
+	DEBUG("first call");
     }
+    
+    for (statePtr->addr = statePtr->addrlist; statePtr->addr != NULL;
+	 statePtr->addr = statePtr->addr->ai_next) {
+	
+        for (statePtr->myaddr = statePtr->myaddrlist; statePtr->myaddr != NULL;
+	     statePtr->myaddr = statePtr->myaddr->ai_next) {
 
-    /*
-     * Do the name lookups for the local and remote addresses.
-     */
+	    DEBUG("inner loop");
 
-    if (!TclCreateSocketAddress(interp, &addrlist, host, port, 0, &errorMsg)
-            || !TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1,
-                    &errorMsg)) {
-        if (addrlist != NULL) {
-            freeaddrinfo(addrlist);
-        }
-        if (interp != NULL) {
-            Tcl_SetObjResult(interp, Tcl_ObjPrintf(
-                    "couldn't open socket: %s", errorMsg));
-        }
-        return NULL;
-    }
-    printaddrinfolist(myaddrlist, "local");
-    printaddrinfolist(addrlist, "remote");
+	    /*
+	     * No need to try combinations of local and remote addresses
+	     * of different families.
+	     */
 
-    infoPtr = NewSocketInfo(INVALID_SOCKET);
-    infoPtr->addrlist = addrlist;
-    infoPtr->myaddrlist = myaddrlist;
-    if (async) {
-	infoPtr->flags |= SOCKET_ASYNC_CONNECT;
-    }
+	    if (statePtr->myaddr->ai_family != statePtr->addr->ai_family) {
+		DEBUG("family mismatch");
+		continue;
+	    }
 
-    /*
-     * Create a new client socket and wrap it in a channel.
-     */
-    DEBUG("");
-    if (CreateClientSocket(interp, infoPtr) != TCL_OK) {
-	TcpCloseProc(infoPtr, NULL);
-	return NULL;
-    }
+	    DEBUG(statePtr->myaddr->ai_family == AF_INET ? "IPv4" : "IPv6");
+	    printaddrinfo(statePtr->myaddr, "~~ from");
+	    printaddrinfo(statePtr->addr,   "~~   to");
 
-    sprintf(channelName, SOCK_TEMPLATE, infoPtr);
+            /*
+             * Close the socket if it is still open from the last unsuccessful
+             * iteration.
+             */	    
+	    if (statePtr->sockets->fd != INVALID_SOCKET) {
+		DEBUG("closesocket");
+		closesocket(statePtr->sockets->fd);
+	    }
 
-    infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
-	    infoPtr, (TCL_READABLE | TCL_WRITABLE));
-    if (TCL_ERROR == Tcl_SetChannelOption(NULL, infoPtr->channel,
-	    "-translation", "auto crlf")) {
-	Tcl_Close(NULL, infoPtr->channel);
-	return NULL;
-    } else if (TCL_ERROR == Tcl_SetChannelOption(NULL, infoPtr->channel,
-	    "-eofchar", "")) {
-	Tcl_Close(NULL, infoPtr->channel);
-	return NULL;
-    }
-    return infoPtr->channel;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_MakeTcpClientChannel --
- *
- *	Creates a Tcl_Channel from an existing client TCP socket.
- *
- * Results:
- *	The Tcl_Channel wrapped around the preexisting TCP socket.
- *
- * Side effects:
- *	None.
- *
- * NOTE: Code contributed by Mark Diekhans ([email-masked])
- *
- *----------------------------------------------------------------------
- */
+	    /* get statePtr lock */
+	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
 
-Tcl_Channel
-Tcl_MakeTcpClientChannel(
-    ClientData sock)		/* The socket to wrap up into a channel. */
-{
-    SocketInfo *infoPtr;
-    char channelName[SOCK_CHAN_LENGTH];
-    ThreadSpecificData *tsdPtr;
+	    /*
+	     * Reset last error from last try
+	     */
+	    statePtr->lastError = 0;
+	    Tcl_SetErrno(0);
+	    
+	    statePtr->sockets->fd = socket(statePtr->myaddr->ai_family, SOCK_STREAM, 0);
+	    
+	    /* Free list lock */
+	    SetEvent(tsdPtr->socketListLock);
 
-    if (TclpHasSockets(NULL) != TCL_OK) {
-	return NULL;
-    }
+	    /* continue on socket creation error */
+	    if (statePtr->sockets->fd == INVALID_SOCKET) {
+		DEBUG("socket() failed");
+		TclWinConvertError((DWORD) WSAGetLastError());
+		continue;
+	    }
+	    
+#ifdef DEBUGGING
+	    fprintf(stderr, "Client socket %d created\n", statePtr->sockets->fd);
+#endif
+	    /*
+	     * Win-NT has a misfeature that sockets are inherited in child
+	     * processes by default. Turn off the inherit bit.
+	     */
 
-    tsdPtr = TclThreadDataKeyGet(&dataKey);
+	    SetHandleInformation((HANDLE) statePtr->sockets->fd, HANDLE_FLAG_INHERIT, 0);
 
-    /*
-     * Set kernel space buffering and non-blocking.
-     */
+	    /*
+	     * Set kernel space buffering
+	     */
 
-    TclSockMinimumBuffers(sock, TCP_BUFFER_SIZE);
+	    TclSockMinimumBuffers((void *) statePtr->sockets->fd, TCP_BUFFER_SIZE);
 
-    infoPtr = NewSocketInfo((SOCKET) sock);
+	    /*
+	     * Try to bind to a local port.
+	     */
 
-    /*
-     * Start watching for read/write events on the socket.
-     */
+	    if (bind(statePtr->sockets->fd, statePtr->myaddr->ai_addr,
+			statePtr->myaddr->ai_addrlen) == SOCKET_ERROR) {
+		DEBUG("bind() failed");
+		TclWinConvertError((DWORD) WSAGetLastError());
+		continue;
+	    }
+	    /*
+	     * For asyncroneous connect set the socket in nonblocking mode
+	     * and activate connect notification
+	     */
+	    if (async_connect) {
+		TcpState *statePtr2;
+		int in_socket_list = 0;
+		/* get statePtr lock */
+		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
 
-    infoPtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE;
-    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)infoPtr);
+		/*
+		 * Check if my statePtr is already in the tsdPtr->socketList
+		 * It is set after this call by TcpThreadActionProc and is set
+		 * on a second round.
+		 *
+		 * If not, we buffer my statePtr in the tsd memory so it is not
+		 * lost by the event procedure
+		 */
+
+		for (statePtr2 = tsdPtr->socketList; statePtr2 != NULL;
+			statePtr2 = statePtr->nextPtr) {
+		    if (statePtr2 == statePtr) {
+			in_socket_list = 1;
+			break;
+		    }
+		}
+		if (!in_socket_list) {
+		    tsdPtr->pendingTcpState = statePtr;
+		}
+		/*
+		 * Set connect mask to connect events
+		 * This is activated by a SOCKET_SELECT message to the notifier
+		 * thread.
+		 */
+		statePtr->selectEvents |= FD_CONNECT;
+		
+		/*
+		 * Free list lock
+		 */
+		SetEvent(tsdPtr->socketListLock);
+
+    		/* activate accept notification */
+		SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+			(LPARAM) statePtr);
+	    }
+
+	    /*
+	     * Attempt to connect to the remote socket.
+	     */
+	    
+	    DEBUG("connect()");
+	    connect(statePtr->sockets->fd, statePtr->addr->ai_addr,
+		    statePtr->addr->ai_addrlen);
+
+	    error = WSAGetLastError();
+	    TclWinConvertError(error);
+
+	    if (async_connect && error == WSAEWOULDBLOCK) {
+		/*
+		 * Asynchroneous connect
+		 */
+		DEBUG("WSAEWOULDBLOCK");
+
+
+		/*
+		 * Remember that we jump back behind this next round
+		 */
+		statePtr->flags |= SOCKET_REENTER_PENDING;
+		return TCL_OK;
+
+	    reenter:
+		DEBUG("reenter");
+		/*
+		 * Re-entry point for async connect after connect event or
+		 * blocking operation
+		 *
+		 * Clear the reenter flag
+		 */
+		statePtr->flags &= ~(SOCKET_REENTER_PENDING);
+		/* get statePtr lock */
+		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+		/* Get signaled connect error */
+		Tcl_SetErrno(statePtr->lastError);
+		/* Clear eventual connect flag */
+		statePtr->selectEvents &= ~(FD_CONNECT);
+		/* Free list lock */
+		SetEvent(tsdPtr->socketListLock);
+	    }
+#ifdef DEBUGGING
+	    fprintf(stderr, "lastError: %d\n", Tcl_GetErrno());
+#endif
+	    /*
+	     * Clear the tsd socket list pointer if we did not wait for
+	     * the FD_CONNECT asyncroneously
+	     */
+	    tsdPtr->pendingTcpState = NULL;
+	    
+	    if (Tcl_GetErrno() == 0) {
+		goto out;
+	    }
+	}
+    }
+
+out:
+    /*
+     * Socket connected or connection failed
+     */
+    DEBUG("connected or finally failed");
+    /* Clear async flag (not really necessary, not used any more) */
+    statePtr->flags &= ~(TCP_ASYNC_CONNECT);
+
+    /*
+     * Final connect failure
+     */
+
+    if ( Tcl_GetErrno() == 0 ) {
+	/*
+	 * Succesfully connected
+	 */
+	/*
+	 * Set up the select mask for read/write events.
+	 */
+	DEBUG("selectEvents = FD_READ | FD_WRITE | FD_CLOSE");    
+	statePtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
+        
+	/*
+	 * Register for interest in events in the select mask. Note that this
+	 * automatically places the socket into non-blocking mode.
+	 */
+        
+	SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+		    (LPARAM) statePtr);
+    } else {
+	/*
+	 * Connect failed
+	 */
+	DEBUG("ERRNO");
 
-    sprintf(channelName, SOCK_TEMPLATE, infoPtr);
-    infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
-	    infoPtr, (TCL_READABLE | TCL_WRITABLE));
-    Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto crlf");
-    return infoPtr->channel;
+	/*
+	 * For async connect schedule a writable event to report the fail.
+	 */
+	if (async_callback) {
+	    /*
+	     * Set up the select mask for read/write events.
+	     */
+	    DEBUG("selectEvents = FD_WRITE for fail writable");    
+	    statePtr->selectEvents = FD_WRITE;
+	    /* get statePtr lock */
+	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+	    /* Clear eventual connect flag */
+	    statePtr->readyEvents |= FD_WRITE;
+	    /* Free list lock */
+	    SetEvent(tsdPtr->socketListLock);
+	}
+	/*
+	 * Error message on syncroneous connect
+	 */
+	if (interp != NULL) {
+	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+		    "couldn't open socket: %s", Tcl_PosixError(interp)));
+	}
+	return TCL_ERROR;
+    }
+    return TCL_OK;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * Tcl_OpenTcpServer --
+ * Tcl_OpenTcpClient --
  *
- *	Opens a TCP server socket and creates a channel around it.
+ *	Opens a TCP client socket and creates a channel around it.
  *
  * Results:
  *	The channel or NULL if failed. An error message is returned in the
  *	interpreter on failure.
  *
  * Side effects:
- *	Opens a server socket and creates a new channel.
+ *	Opens a client socket and creates a new channel.
  *
  *----------------------------------------------------------------------
  */
 
 Tcl_Channel
-Tcl_OpenTcpServer(
-    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
+Tcl_OpenTcpClient(
+    Tcl_Interp *interp,		/* For error reporting; can be NULL. */
     int port,			/* Port number to open. */
-    const char *host,		/* Name of local host. */
-    Tcl_TcpAcceptProc *acceptProc,
-				/* Callback for accepting connections from new
-				 * clients. */
-    ClientData acceptProcData)	/* Data for the callback. */
+    const char *host,		/* Host on which to open port. */
+    const char *myaddr,		/* Client-side address */
+    int myport,			/* Client-side port */
+    int async)			/* If nonzero, attempt to do an asynchronous
+				 * connect. Otherwise we do a blocking
+				 * connect. */
 {
-    SOCKET sock = INVALID_SOCKET;
-    unsigned short chosenport = 0;
-    struct addrinfo *addrPtr;	/* Socket address to listen on. */
-    SocketInfo *infoPtr = NULL;	/* The returned value. */
-    struct addrinfo *addrlist = NULL;
-    char channelName[SOCK_CHAN_LENGTH];
-    u_long flag = 1;		/* Indicates nonblocking mode. */
+    TcpState *statePtr;
     const char *errorMsg = NULL;
-    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+    struct addrinfo *addrlist = NULL, *myaddrlist = NULL;
+    char channelName[SOCK_CHAN_LENGTH];
 
     if (TclpHasSockets(interp) != TCL_OK) {
 	return NULL;
@@ -1830,116 +1855,270 @@ Tcl_OpenTcpServer(
     }
 
     /*
-     * Construct the addresses for each end of the socket.
+     * Do the name lookups for the local and remote addresses.
      */
 
-    if (!TclCreateSocketAddress(interp, &addrlist, host, port, 1, &errorMsg)) {
-	goto error;
+    if (!TclCreateSocketAddress(interp, &addrlist, host, port, 0, &errorMsg)
+            || !TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1,
+                    &errorMsg)) {
+        if (addrlist != NULL) {
+            freeaddrinfo(addrlist);
+        }
+        if (interp != NULL) {
+            Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+                    "couldn't open socket: %s", errorMsg));
+        }
+        return NULL;
     }
+    printaddrinfolist(myaddrlist, "local");
+    printaddrinfolist(addrlist, "remote");
 
-    for (addrPtr = addrlist; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
-	sock = socket(addrPtr->ai_family, addrPtr->ai_socktype,
-                addrPtr->ai_protocol);
-	if (sock == INVALID_SOCKET) {
-	    TclWinConvertError((DWORD) WSAGetLastError());
-	    continue;
-	}
-	
-	/*
-	 * Win-NT has a misfeature that sockets are inherited in child
-	 * processes by default. Turn off the inherit bit.
-	 */
-	
-	SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
-	
-	/*
-	 * Set kernel space buffering
-	 */
-	
-	TclSockMinimumBuffers((void *)sock, TCP_BUFFER_SIZE);
-	
-	/*
-	 * Make sure we use the same port when opening two server sockets
-	 * for IPv4 and IPv6.
-	 *
-	 * As sockaddr_in6 uses the same offset and size for the port
-	 * member as sockaddr_in, we can handle both through the IPv4 API.
-	 */
-	
-	if (port == 0 && chosenport != 0) {
-	    ((struct sockaddr_in *) addrPtr->ai_addr)->sin_port =
-		htons(chosenport);
-	}
-	
-	/*
-	 * Bind to the specified port. Note that we must not call
-	 * setsockopt with SO_REUSEADDR because Microsoft allows addresses
-	 * to be reused even if they are still in use.
-	 *
-	 * Bind should not be affected by the socket having already been
-	 * set into nonblocking mode. If there is trouble, this is one
-	 * place to look for bugs.
-	 */
-	
-	if (bind(sock, addrPtr->ai_addr, addrPtr->ai_addrlen)
-	    == SOCKET_ERROR) {
-	    TclWinConvertError((DWORD) WSAGetLastError());
-	    closesocket(sock);
-	    continue;
-	}
-	if (port == 0 && chosenport == 0) {
-	    address sockname;
-	    socklen_t namelen = sizeof(sockname);
-	    
-	    /*
-	     * Synchronize port numbers when binding to port 0 of multiple
-	     * addresses.
-	     */
-	    
-	    if (getsockname(sock, &sockname.sa, &namelen) >= 0) {
-		chosenport = ntohs(sockname.sa4.sin_port);
-	    }
-	}
-	
-	/*
-	 * Set the maximum number of pending connect requests to the max
-	 * value allowed on each platform (Win32 and Win32s may be
-	 * different, and there may be differences between TCP/IP stacks).
-	 */
-	
-	if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
-	    TclWinConvertError((DWORD) WSAGetLastError());
-	    closesocket(sock);
-	    continue;
-	}
-	
-	if (infoPtr == NULL) {
-	    /*
-	     * Add this socket to the global list of sockets.
-	     */
-	    infoPtr = NewSocketInfo(sock);
-	} else {
-	    AddSocketInfoFd( infoPtr, sock );
-	}
+    statePtr = NewSocketInfo(INVALID_SOCKET);
+    statePtr->addrlist = addrlist;
+    statePtr->myaddrlist = myaddrlist;
+    if (async) {
+	statePtr->flags |= TCP_ASYNC_CONNECT;
     }
 
-error:
-     if (addrlist != NULL) {
-	freeaddrinfo(addrlist);
+    /*
+     * Create a new client socket and wrap it in a channel.
+     */
+    DEBUG("");
+    if (CreateClientSocket(interp, statePtr) != TCL_OK) {
+	TcpCloseProc(statePtr, NULL);
+	return NULL;
+    }
+
+    sprintf(channelName, SOCK_TEMPLATE, statePtr);
+
+    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+	    statePtr, (TCL_READABLE | TCL_WRITABLE));
+    if (TCL_ERROR == Tcl_SetChannelOption(NULL, statePtr->channel,
+	    "-translation", "auto crlf")) {
+	Tcl_Close(NULL, statePtr->channel);
+	return NULL;
+    } else if (TCL_ERROR == Tcl_SetChannelOption(NULL, statePtr->channel,
+	    "-eofchar", "")) {
+	Tcl_Close(NULL, statePtr->channel);
+	return NULL;
+    }
+    return statePtr->channel;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_MakeTcpClientChannel --
+ *
+ *	Creates a Tcl_Channel from an existing client TCP socket.
+ *
+ * Results:
+ *	The Tcl_Channel wrapped around the preexisting TCP socket.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Channel
+Tcl_MakeTcpClientChannel(
+    ClientData sock)		/* The socket to wrap up into a channel. */
+{
+    TcpState *statePtr;
+    char channelName[SOCK_CHAN_LENGTH];
+    ThreadSpecificData *tsdPtr;
+
+    if (TclpHasSockets(NULL) != TCL_OK) {
+	return NULL;
+    }
+
+    tsdPtr = TclThreadDataKeyGet(&dataKey);
+
+    /*
+     * Set kernel space buffering and non-blocking.
+     */
+
+    TclSockMinimumBuffers(sock, TCP_BUFFER_SIZE);
+
+    statePtr = NewSocketInfo((SOCKET) sock);
+
+    /*
+     * Start watching for read/write events on the socket.
+     */
+
+    statePtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE;
+    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr);
+
+    sprintf(channelName, SOCK_TEMPLATE, statePtr);
+    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+	    statePtr, (TCL_READABLE | TCL_WRITABLE));
+    Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
+    return statePtr->channel;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_OpenTcpServer --
+ *
+ *	Opens a TCP server socket and creates a channel around it.
+ *
+ * Results:
+ *	The channel or NULL if failed. If an error occurred, an error message
+ *	is left in the interp's result if interp is not NULL.
+ *
+ * Side effects:
+ *	Opens a server socket and creates a new channel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Channel
+Tcl_OpenTcpServer(
+    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
+    int port,			/* Port number to open. */
+    const char *myHost,		/* Name of local host. */
+    Tcl_TcpAcceptProc *acceptProc,
+				/* Callback for accepting connections from new
+				 * clients. */
+    ClientData acceptProcData)	/* Data for the callback. */
+{
+    SOCKET sock = INVALID_SOCKET;
+    unsigned short chosenport = 0;
+    struct addrinfo *addrlist = NULL;
+    struct addrinfo *addrPtr;	/* Socket address to listen on. */
+    TcpState *statePtr = NULL;	/* The returned value. */
+    char channelName[SOCK_CHAN_LENGTH];
+    u_long flag = 1;		/* Indicates nonblocking mode. */
+    const char *errorMsg = NULL;
+    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+
+    if (TclpHasSockets(interp) != TCL_OK) {
+	return NULL;
+    }
+
+    /*
+     * Check that WinSock is initialized; do not call it if not, to prevent
+     * system crashes. This can happen at exit time if the exit handler for
+     * WinSock ran before other exit handlers that want to use sockets.
+     */
+
+    if (!SocketsEnabled()) {
+	return NULL;
+    }
+
+    /*
+     * Construct the addresses for each end of the socket.
+     */
+
+    if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) {
+	goto error;
+    }
+
+    for (addrPtr = addrlist; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
+	sock = socket(addrPtr->ai_family, addrPtr->ai_socktype,
+                addrPtr->ai_protocol);
+	if (sock == INVALID_SOCKET) {
+	    TclWinConvertError((DWORD) WSAGetLastError());
+	    continue;
+	}
+	
+	/*
+	 * Win-NT has a misfeature that sockets are inherited in child
+	 * processes by default. Turn off the inherit bit.
+	 */
+	
+	SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
+	
+	/*
+	 * Set kernel space buffering
+	 */
+	
+	TclSockMinimumBuffers((void *)sock, TCP_BUFFER_SIZE);
+	
+	/*
+	 * Make sure we use the same port when opening two server sockets
+	 * for IPv4 and IPv6.
+	 *
+	 * As sockaddr_in6 uses the same offset and size for the port
+	 * member as sockaddr_in, we can handle both through the IPv4 API.
+	 */
+	
+	if (port == 0 && chosenport != 0) {
+	    ((struct sockaddr_in *) addrPtr->ai_addr)->sin_port =
+		htons(chosenport);
+	}
+	
+	/*
+	 * Bind to the specified port. Note that we must not call
+	 * setsockopt with SO_REUSEADDR because Microsoft allows addresses
+	 * to be reused even if they are still in use.
+	 *
+	 * Bind should not be affected by the socket having already been
+	 * set into nonblocking mode. If there is trouble, this is one
+	 * place to look for bugs.
+	 */
+	
+	if (bind(sock, addrPtr->ai_addr, addrPtr->ai_addrlen)
+	    == SOCKET_ERROR) {
+	    TclWinConvertError((DWORD) WSAGetLastError());
+	    closesocket(sock);
+	    continue;
+	}
+	if (port == 0 && chosenport == 0) {
+	    address sockname;
+	    socklen_t namelen = sizeof(sockname);
+	    
+	    /*
+	     * Synchronize port numbers when binding to port 0 of multiple
+	     * addresses.
+	     */
+	    
+	    if (getsockname(sock, &sockname.sa, &namelen) >= 0) {
+		chosenport = ntohs(sockname.sa4.sin_port);
+	    }
+	}
+	
+	/*
+	 * Set the maximum number of pending connect requests to the max
+	 * value allowed on each platform (Win32 and Win32s may be
+	 * different, and there may be differences between TCP/IP stacks).
+	 */
+	
+	if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
+	    TclWinConvertError((DWORD) WSAGetLastError());
+	    closesocket(sock);
+	    continue;
+	}
+	
+	if (statePtr == NULL) {
+	    /*
+	     * Add this socket to the global list of sockets.
+	     */
+	    statePtr = NewSocketInfo(sock);
+	} else {
+	    AddSocketInfoFd( statePtr, sock );
+	}
+    }
+
+error:
+    if (addrlist != NULL) {
+	freeaddrinfo(addrlist);
     }
 
-    if (infoPtr != NULL) {
+    if (statePtr != NULL) {
 
-	infoPtr->acceptProc = acceptProc;
-	infoPtr->acceptProcData = acceptProcData;
-	sprintf(channelName, SOCK_TEMPLATE, infoPtr);
-	infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
-					    infoPtr, 0);	
+	statePtr->acceptProc = acceptProc;
+	statePtr->acceptProcData = acceptProcData;
+	sprintf(channelName, SOCK_TEMPLATE, statePtr);
+	statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+		statePtr, 0);
 	/*
 	 * Set up the select mask for connection request events.
 	 */
 	
-	infoPtr->selectEvents = FD_ACCEPT;
+	statePtr->selectEvents = FD_ACCEPT;
 	
 	/*
 	 * Register for interest in events in the select mask. Note that this
@@ -1948,13 +2127,13 @@ error:
 
 	ioctlsocket(sock, (long) FIONBIO, &flag);
 	SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
-		    (LPARAM) infoPtr);
-	if (Tcl_SetChannelOption(interp, infoPtr->channel, "-eofchar", "")
+		    (LPARAM) statePtr);
+	if (Tcl_SetChannelOption(interp, statePtr->channel, "-eofchar", "")
 	    == TCL_ERROR) {
-	    Tcl_Close(NULL, infoPtr->channel);
+	    Tcl_Close(NULL, statePtr->channel);
 	    return NULL;
 	}
-	return infoPtr->channel;
+	return statePtr->channel;
     }
 
     if (interp != NULL) {
@@ -1973,28 +2152,27 @@ error:
  *----------------------------------------------------------------------
  *
  * TcpAccept --
- *
- *	Creates a channel for a newly accepted socket connection. This is 
- *	called by SocketEventProc and it in turns calls the registered 
- *	accept function.
+ *	Accept a TCP socket connection.	 This is called by the event loop.
  *
  * Results:
  *	None.
  *
  * Side effects:
- *	Invokes the accept proc which may invoke arbitrary Tcl code.
+ *	Creates a new connection socket. Calls the registered callback for the
+ *	connection acceptance mechanism.
  *
  *----------------------------------------------------------------------
  */
 
+	/* ARGSUSED */
 static void
 TcpAccept(
     TcpFdList *fds,	/* Server socket that accepted newSocket. */
     SOCKET newSocket,   /* Newly accepted socket. */
     address addr)       /* Address of new socket. */
 {
-    SocketInfo *newInfoPtr;
-    SocketInfo *infoPtr = fds->infoPtr;
+    TcpState *newInfoPtr;
+    TcpState *statePtr = fds->statePtr;
     int len = sizeof(addr);
     char channelName[SOCK_CHAN_LENGTH];
     char host[NI_MAXHOST], port[NI_MAXSERV];
@@ -2039,10 +2217,10 @@ TcpAccept(
      * Invoke the accept callback function.
      */
 
-    if (infoPtr->acceptProc != NULL) {
+    if (statePtr->acceptProc != NULL) {
 	getnameinfo(&(addr.sa), len, host, sizeof(host), port, sizeof(port),
                     NI_NUMERICHOST|NI_NUMERICSERV);
-	infoPtr->acceptProc(infoPtr->acceptProcData, newInfoPtr->channel,
+	statePtr->acceptProc(statePtr->acceptProcData, newInfoPtr->channel,
 			    host, atoi(port));
     }
 }
@@ -2050,707 +2228,652 @@ TcpAccept(
 /*
  *----------------------------------------------------------------------
  *
- * TcpInputProc --
+ * InitSockets --
  *
- *	This function is called by the generic IO level to read data from a
- *	socket based channel.
+ *	Initialize the socket module.  If winsock startup is successful,
+ *	registers the event window for the socket notifier code.
+ *
+ *	Assumes socketMutex is held.
  *
  * Results:
- *	The number of bytes read or -1 on error.
+ *	None.
  *
  * Side effects:
- *	Consumes input from the socket.
+ *	Initializes winsock, registers a new window class and creates a
+ *	window for use in asynchronous socket notification.
  *
  *----------------------------------------------------------------------
  */
 
-static int
-TcpInputProc(
-    ClientData instanceData,	/* The socket state. */
-    char *buf,			/* Where to store data. */
-    int toRead,			/* Maximum number of bytes to read. */
-    int *errorCodePtr)		/* Where to store error codes. */
+static void
+InitSockets(void)
 {
-    SocketInfo *infoPtr = instanceData;
-    int bytesRead;
-    DWORD error;
+    DWORD id, err;
+    WSADATA wsaData;
     ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
 
-    *errorCodePtr = 0;
+    if (!initialized) {
+	initialized = 1;
+	TclCreateLateExitHandler(SocketExitHandler, NULL);
 
-    /*
-     * Check that WinSock is initialized; do not call it if not, to prevent
-     * system crashes. This can happen at exit time if the exit handler for
-     * WinSock ran before other exit handlers that want to use sockets.
-     */
+	/*
+	 * Create the async notification window with a new class. We must
+	 * create a new class to avoid a Windows 95 bug that causes us to get
+	 * the wrong message number for socket events if the message window is
+	 * a subclass of a static control.
+	 */
 
-    if (!SocketsEnabled()) {
-	*errorCodePtr = EFAULT;
-	return -1;
-    }
+	windowClass.style = 0;
+	windowClass.cbClsExtra = 0;
+	windowClass.cbWndExtra = 0;
+	windowClass.hInstance = TclWinGetTclInstance();
+	windowClass.hbrBackground = NULL;
+	windowClass.lpszMenuName = NULL;
+	windowClass.lpszClassName = classname;
+	windowClass.lpfnWndProc = SocketProc;
+	windowClass.hIcon = NULL;
+	windowClass.hCursor = NULL;
 
-    /*
-     * First check to see if EOF was already detected, to prevent calling the
-     * socket stack after the first time EOF is detected.
-     */
-
-    if (infoPtr->flags & SOCKET_EOF) {
-	return 0;
-    }
-
-    /*
-     * Check if there is an async connect running.
-     * For blocking sockets terminate connect, otherwise do one step.
-     * For a non blocking socket return EWOULDBLOCK if connect not terminated
-     */
-
-    if ( (infoPtr->flags & SOCKET_REENTER_PENDING)
-	    && !WaitForConnect(infoPtr, errorCodePtr,
-		! ( infoPtr->flags & TCP_ASYNC_SOCKET ))) {
-	return -1;
-    }
-
-    /*
-     * No EOF, and it is connected, so try to read more from the socket. Note
-     * that we clear the FD_READ bit because read events are level triggered
-     * so a new event will be generated if there is still data available to be
-     * read. We have to simulate blocking behavior here since we are always
-     * using non-blocking sockets.
-     */
-
-    while (1) {
-	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
-		(WPARAM) UNSELECT, (LPARAM) infoPtr);
-	/* single fd operation: this proc is only called for a connected socket. */
-	bytesRead = recv(infoPtr->sockets->fd, buf, toRead, 0);
-	infoPtr->readyEvents &= ~(FD_READ);
+	if (!RegisterClass(&windowClass)) {
+	    TclWinConvertError(GetLastError());
+	    goto initFailure;
+	}
 
 	/*
-	 * Check for end-of-file condition or successful read.
+	 * Initialize the winsock library and check the interface version
+	 * actually loaded. We only ask for the 1.1 interface and do require
+	 * that it not be less than 1.1.
 	 */
 
-	if (bytesRead == 0) {
-	    infoPtr->flags |= SOCKET_EOF;
-	}
-	if (bytesRead != SOCKET_ERROR) {
-	    break;
+	err = WSAStartup((WORD) MAKEWORD(WSA_VERSION_MAJOR,WSA_VERSION_MINOR),
+		&wsaData);
+	if (err != 0) {
+	    TclWinConvertError(err);
+	    goto initFailure;
 	}
 
 	/*
-	 * If an error occurs after the FD_CLOSE has arrived, then ignore the
-	 * error and report an EOF.
+	 * Note the byte positions ae swapped for the comparison, so that
+	 * 0x0002 (2.0, MAKEWORD(2,0)) doesn't look less than 0x0101 (1.1). We
+	 * want the comparison to be 0x0200 < 0x0101.
 	 */
 
-	if (infoPtr->readyEvents & FD_CLOSE) {
-	    infoPtr->flags |= SOCKET_EOF;
-	    bytesRead = 0;
-	    break;
+	if (MAKEWORD(HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion))
+		< MAKEWORD(WSA_VERSION_MINOR, WSA_VERSION_MAJOR)) {
+	    TclWinConvertError(WSAVERNOTSUPPORTED);
+	    WSACleanup();
+	    goto initFailure;
 	}
+    }
 
-	error = WSAGetLastError();
+    /*
+     * Check for per-thread initialization.
+     */
 
-	/*
-	 * If an RST comes, then ignore the error and report an EOF just like
-	 * on unix.
-	 */
+    if (tsdPtr != NULL) {
+	return;
+    }
 
-	if (error == WSAECONNRESET) {
-	    infoPtr->flags |= SOCKET_EOF;
-	    bytesRead = 0;
-	    break;
-	}
+    /*
+     * OK, this thread has never done anything with sockets before.  Construct
+     * a worker thread to handle asynchronous events related to sockets
+     * assigned to _this_ thread.
+     */
 
-	/*
-	 * Check for error condition or underflow in non-blocking case.
-	 */
+    tsdPtr = TCL_TSD_INIT(&dataKey);
+    tsdPtr->pendingTcpState = NULL;
+    tsdPtr->socketList = NULL;
+    tsdPtr->hwnd       = NULL;
+    tsdPtr->threadId   = Tcl_GetCurrentThread();
+    tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (tsdPtr->readyEvent == NULL) {
+	goto initFailure;
+    }
+    tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL);
+    if (tsdPtr->socketListLock == NULL) {
+	goto initFailure;
+    }
+    tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread, tsdPtr, 0,
+	    &id);
+    if (tsdPtr->socketThread == NULL) {
+	goto initFailure;
+    }
 
-	if ((infoPtr->flags & TCP_ASYNC_SOCKET) || (error != WSAEWOULDBLOCK)) {
-	    TclWinConvertError(error);
-	    *errorCodePtr = Tcl_GetErrno();
-	    bytesRead = -1;
-	    break;
-	}
+    SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST);
 
-	/*
-	 * In the blocking case, wait until the file becomes readable or
-	 * closed and try again.
-	 */
+    /*
+     * Wait for the thread to signal when the window has been created and if
+     * it is ready to go.
+     */
 
-	if (!WaitForSocketEvent(infoPtr, FD_READ|FD_CLOSE, errorCodePtr)) {
-	    bytesRead = -1;
-	    break;
-	}
+    WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
+
+    if (tsdPtr->hwnd == NULL) {
+	goto initFailure;	/* Trouble creating the window. */
     }
 
-    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)infoPtr);
+    Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
+    return;
 
-    return bytesRead;
+  initFailure:
+    TclpFinalizeSockets();
+    initialized = -1;
+    return;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TcpOutputProc --
+ * SocketsEnabled --
  *
- *	This function is called by the generic IO level to write data to a
- *	socket based channel.
+ *	Check that the WinSock was successfully initialized.
  *
  * Results:
- *	The number of bytes written or -1 on failure.
+ *	1 if it is.
  *
  * Side effects:
- *	Produces output on the socket.
+ *	None.
  *
  *----------------------------------------------------------------------
  */
 
+    /* ARGSUSED */
 static int
-TcpOutputProc(
-    ClientData instanceData,	/* The socket state. */
-    const char *buf,		/* Where to get data. */
-    int toWrite,		/* Maximum number of bytes to write. */
-    int *errorCodePtr)		/* Where to store error codes. */
+SocketsEnabled(void)
 {
-    SocketInfo *infoPtr = instanceData;
-    int bytesWritten;
-    DWORD error;
-    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+    int enabled;
 
-    *errorCodePtr = 0;
+    Tcl_MutexLock(&socketMutex);
+    enabled = (initialized == 1);
+    Tcl_MutexUnlock(&socketMutex);
+    return enabled;
+}
 
-    /*
-     * Check that WinSock is initialized; do not call it if not, to prevent
-     * system crashes. This can happen at exit time if the exit handler for
-     * WinSock ran before other exit handlers that want to use sockets.
-     */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SocketExitHandler --
+ *
+ *	Callback invoked during exit clean up to delete the socket
+ *	communication window and to release the WinSock DLL.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
 
-    if (!SocketsEnabled()) {
-	*errorCodePtr = EFAULT;
-	return -1;
-    }
+    /* ARGSUSED */
+static void
+SocketExitHandler(
+    ClientData clientData)		/* Not used. */
+{
+    Tcl_MutexLock(&socketMutex);
 
     /*
-     * Check if there is an async connect running.
-     * For blocking sockets terminate connect, otherwise do one step.
-     * For a non blocking socket return EWOULDBLOCK if connect not terminated
+     * Make sure the socket event handling window is cleaned-up for, at
+     * most, this thread.
      */
 
-    if ( (infoPtr->flags & SOCKET_REENTER_PENDING)
-	    && !WaitForConnect(infoPtr, errorCodePtr,
-		! ( infoPtr->flags & TCP_ASYNC_SOCKET ))) {
-	return -1;
-    }
-
-    while (1) {
-	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
-		(WPARAM) UNSELECT, (LPARAM) infoPtr);
-
-	/* single fd operation: this proc is only called for a connected socket. */
-	bytesWritten = send(infoPtr->sockets->fd, 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 (infoPtr->watchEvents & FD_WRITE) {
-		Tcl_Time blockTime = { 0, 0 };
-		Tcl_SetMaxBlockTime(&blockTime);
-	    }
-	    break;
-	}
-
-	/*
-	 * Check for error condition or overflow. In the event of overflow, we
-	 * need to clear the FD_WRITE flag so we can detect the next writable
-	 * event. Note that Windows only sends a new writable event after a
-	 * send fails with WSAEWOULDBLOCK.
-	 */
-
-	error = WSAGetLastError();
-	if (error == WSAEWOULDBLOCK) {
-	    infoPtr->readyEvents &= ~(FD_WRITE);
-	    if (infoPtr->flags & TCP_ASYNC_SOCKET) {
-		*errorCodePtr = EWOULDBLOCK;
-		bytesWritten = -1;
-		break;
-	    }
-	} else {
-	    TclWinConvertError(error);
-	    *errorCodePtr = Tcl_GetErrno();
-	    bytesWritten = -1;
-	    break;
-	}
-
-	/*
-	 * In the blocking case, wait until the file becomes writable or
-	 * closed and try again.
-	 */
-
-	if (!WaitForSocketEvent(infoPtr, FD_WRITE|FD_CLOSE, errorCodePtr)) {
-	    bytesWritten = -1;
-	    break;
-	}
-    }
-
-    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)infoPtr);
-
-    return bytesWritten;
+    TclpFinalizeSockets();
+    UnregisterClass(classname, TclWinGetTclInstance());
+    WSACleanup();
+    initialized = 0;
+    Tcl_MutexUnlock(&socketMutex);
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TcpSetOptionProc --
+ * SocketSetupProc --
  *
- *	Sets Tcp channel specific options.
+ *	This function is invoked before Tcl_DoOneEvent blocks waiting for an
+ *	event.
  *
  * Results:
- *	None, unless an error happens.
+ *	None.
  *
  * Side effects:
- *	Changes attributes of the socket at the system level.
+ *	Adjusts the block time if needed.
  *
  *----------------------------------------------------------------------
  */
 
-static int
-TcpSetOptionProc(
-    ClientData instanceData,	/* Socket state. */
-    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
-    const char *optionName,	/* Name of the option to set. */
-    const char *value)		/* New value for option. */
+void
+SocketSetupProc(
+    ClientData data,		/* Not used. */
+    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
 {
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
-    SocketInfo *infoPtr = instanceData;
-    SOCKET sock;
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+    TcpState *statePtr;
+    Tcl_Time blockTime = { 0, 0 };
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+    if (!(flags & TCL_FILE_EVENTS)) {
+	return;
+    }
 
     /*
-     * Check that WinSock is initialized; do not call it if not, to prevent
-     * system crashes. This can happen at exit time if the exit handler for
-     * WinSock ran before other exit handlers that want to use sockets.
+     * Check to see if there is a ready socket.	 If so, poll.
      */
-
-    if (!SocketsEnabled()) {
-	if (interp) {
-	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
-		    "winsock is not initialized", -1));
+    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+    for (statePtr = tsdPtr->socketList; statePtr != NULL;
+	    statePtr = statePtr->nextPtr) {
+	if (statePtr->readyEvents &
+	    (statePtr->watchEvents | FD_CONNECT | FD_ACCEPT)
+	) {
+	    DEBUG("Tcl_SetMaxBlockTime");
+	    Tcl_SetMaxBlockTime(&blockTime);
+	    break;
 	}
-	return TCL_ERROR;
     }
+    SetEvent(tsdPtr->socketListLock);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SocketCheckProc --
+ *
+ *	This function is called by Tcl_DoOneEvent to check the socket event
+ *	source for events.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	May queue an event.
+ *
+ *----------------------------------------------------------------------
+ */
 
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
-    #error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat infoPtr->sockets as single fd or list"
-    sock = infoPtr->sockets->fd;
+static void
+SocketCheckProc(
+    ClientData data,		/* Not used. */
+    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
+{
+    TcpState *statePtr;
+    SocketEvent *evPtr;
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 
-    if (!strcasecmp(optionName, "-keepalive")) {
-	BOOL val = FALSE;
-	int boolVar, rtn;
+    if (!(flags & TCL_FILE_EVENTS)) {
+	return;
+    }
 
-	if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
-	    return TCL_ERROR;
-	}
-	if (boolVar) {
-	    val = TRUE;
-	}
-	rtn = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
-		(const char *) &val, sizeof(BOOL));
-	if (rtn != 0) {
-	    TclWinConvertError(WSAGetLastError());
-	    if (interp) {
-		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
-			"couldn't set socket option: %s",
-			Tcl_PosixError(interp)));
-	    }
-	    return TCL_ERROR;
-	}
-	return TCL_OK;
-    } else if (!strcasecmp(optionName, "-nagle")) {
-	BOOL val = FALSE;
-	int boolVar, rtn;
+    /*
+     * Queue events for any ready sockets that don't already have events
+     * queued (caused by persistent states that won't generate WinSock
+     * events).
+     */
 
-	if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
-	    return TCL_ERROR;
-	}
-	if (!boolVar) {
-	    val = TRUE;
-	}
-	rtn = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
-		(const char *) &val, sizeof(BOOL));
-	if (rtn != 0) {
-	    TclWinConvertError(WSAGetLastError());
-	    if (interp) {
-		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
-			"couldn't set socket option: %s",
-			Tcl_PosixError(interp)));
-	    }
-	    return TCL_ERROR;
+    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+    for (statePtr = tsdPtr->socketList; statePtr != NULL;
+	    statePtr = statePtr->nextPtr) {
+	DEBUG("Socket loop");
+	if ((statePtr->readyEvents &
+		(statePtr->watchEvents | FD_CONNECT | FD_ACCEPT))
+	    && !(statePtr->flags & SOCKET_PENDING)
+	) {
+	    DEBUG("Event found");
+	    statePtr->flags |= SOCKET_PENDING;
+	    evPtr = ckalloc(sizeof(SocketEvent));
+	    evPtr->header.proc = SocketEventProc;
+	    evPtr->socket = statePtr->sockets->fd;
+	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
 	}
-	return TCL_OK;
     }
-
-    return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
-#else
-    return Tcl_BadChannelOption(interp, optionName, "");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+    SetEvent(tsdPtr->socketListLock);
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TcpGetOptionProc --
- *
- *	Computes an option value for a TCP socket based channel, or a list of
- *	all options and their values.
+ * SocketEventProc --
  *
- *	Note: This code is based on code contributed by John Haxby.
+ *	This function is called by Tcl_ServiceEvent when a socket event
+ *	reaches the front of the event queue. This function is responsible for
+ *	notifying the generic channel code.
  *
  * Results:
- *	A standard Tcl result. The value of the specified option or a list of
- *	all options and their values is returned in the supplied DString.
+ *	Returns 1 if the event was handled, meaning it should be removed from
+ *	the queue. Returns 0 if the event was not handled, meaning it should
+ *	stay on the queue. The only time the event isn't handled is if the
+ *	TCL_FILE_EVENTS flag bit isn't set.
  *
  * Side effects:
- *	None.
+ *	Whatever the channel callback functions do.
  *
  *----------------------------------------------------------------------
  */
 
 static int
-TcpGetOptionProc(
-    ClientData instanceData,	/* Socket state. */
-    Tcl_Interp *interp,		/* For error reporting - can be NULL */
-    const char *optionName,	/* Name of the option to retrieve the value
-				 * for, or NULL to get all options and their
-				 * values. */
-    Tcl_DString *dsPtr)		/* Where to store the computed value;
-				 * initialized by caller. */
+SocketEventProc(
+    Tcl_Event *evPtr,		/* Event to service. */
+    int flags)			/* Flags that indicate what events to handle,
+				 * such as TCL_FILE_EVENTS. */
 {
-    SocketInfo *infoPtr = instanceData;
-    char host[NI_MAXHOST], port[NI_MAXSERV];
-    SOCKET sock;
-    size_t len = 0;
-    int reverseDNS = 0;
-#define SUPPRESS_RDNS_VAR "::tcl::unsupported::noReverseDNS"
+    TcpState *statePtr = NULL; /* DEBUG */
+    SocketEvent *eventPtr = (SocketEvent *) evPtr;
+    int mask = 0, events;
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+    TcpFdList *fds;
+    SOCKET newSocket;
+    address addr;
+    int len;
+
+    DEBUG("");
+    if (!(flags & TCL_FILE_EVENTS)) {
+	return 0;
+    }
 
     /*
-     * Check that WinSock is initialized; do not call it if not, to prevent
-     * system crashes. This can happen at exit time if the exit handler for
-     * WinSock ran before other exit handlers that want to use sockets.
+     * Find the specified socket on the socket list.
      */
 
-    if (!SocketsEnabled()) {
-	if (interp) {
-	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
-		    "winsock is not initialized", -1));
+    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+    for (statePtr = tsdPtr->socketList; statePtr != NULL;
+	    statePtr = statePtr->nextPtr) {
+	if (statePtr->sockets->fd == eventPtr->socket) {
+	    break;
 	}
-	return TCL_ERROR;
-    }
-
-    sock = infoPtr->sockets->fd;
-    if (optionName != NULL) {
-	len = strlen(optionName);
     }
 
-    if ((len > 1) && (optionName[1] == 'e') &&
-	    (strncmp(optionName, "-error", len) == 0)) {
+    /*
+     * Discard events that have gone stale.
+     */
 
-	if ( (infoPtr->flags & SOCKET_REENTER_PENDING) ) {
+    if (!statePtr) {
+        SetEvent(tsdPtr->socketListLock);
+	return 1;
+    }
 
-	    /*
-	     * Asyncroneous connect is running.
-	     * Process it one step without blocking.
-	     * Return its error or nothing if connect not
-	     * terminated.
-	     */
+    statePtr->flags &= ~SOCKET_PENDING;
 
-	    int errorCode;
-	    if (!WaitForConnect(infoPtr, &errorCode, 0)
-		    && errorCode != EWOULDBLOCK) {
-		/* connect terminated with error */
-		Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errorCode), -1);
-	    }
+    /* Continue async connect if pending and ready */
+    if ( statePtr->readyEvents & FD_CONNECT ) {
+	statePtr->readyEvents &= ~(FD_CONNECT);
+	DEBUG("FD_CONNECT");
+        if ( statePtr->flags & SOCKET_REENTER_PENDING ) {
+	    SetEvent(tsdPtr->socketListLock);
+	    CreateClientSocket(NULL, statePtr);
+	    return 1;
+	}
+    }
 
-	} else {
-	    int optlen;
-	    int ret;
-    	    DWORD err;
+    /*
+     * Handle connection requests directly.
+     */
+    if (statePtr->readyEvents & FD_ACCEPT) {
+	for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
 
 	    /*
-	     * Populater the err Variable with a possix error
-	     */
-	    optlen = sizeof(int);
-	    ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
-		    (char *)&err, &optlen);
-	    /*
-	     * The error was not returned directly but should be
-	     * taken from WSA
-	     */
-	    if (ret == SOCKET_ERROR) {
-		err = WSAGetLastError();
-	    }
-	    /*
-	     * Return error message
-	     */
-	    if (err) {
-		TclWinConvertError(err);
-		Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
-	    }
-	}
-	return TCL_OK;
-    }
+	    * Accept the incoming connection request.
+	    */
+	    len = sizeof(address);
 
-    if (interp != NULL && Tcl_GetVar(interp, SUPPRESS_RDNS_VAR, 0) != NULL) {
-	reverseDNS = NI_NUMERICHOST;
-    }
+	    newSocket = accept(fds->fd, &(addr.sa), &len);
 
-    if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
-	    (strncmp(optionName, "-peername", len) == 0))) {
-	address peername;
-	socklen_t size = sizeof(peername);
+	    /* On Tcl server sockets with multiple OS fds we loop over the fds trying
+	     * an accept() on each, so we expect INVALID_SOCKET.  There are also other
+	     * network stack conditions that can result in FD_ACCEPT but a subsequent
+	     * failure on accept() by the time we get around to it.
+	     * Access to sockets (acceptEventCount, readyEvents) in socketList
+	     * is still protected by the lock (prevents reintroduction of 
+	     * SF Tcl Bug 3056775.
+	     */
 
-	if (getpeername(sock, (LPSOCKADDR) &(peername.sa), &size) == 0) {
-	    if (len == 0) {
-		Tcl_DStringAppendElement(dsPtr, "-peername");
-		Tcl_DStringStartSublist(dsPtr);
+	    if (newSocket == INVALID_SOCKET) {
+		/* int err = WSAGetLastError(); */
+		continue;
 	    }
 
-	    getnameinfo(&(peername.sa), size, host, sizeof(host),
-		    NULL, 0, NI_NUMERICHOST);
-	    Tcl_DStringAppendElement(dsPtr, host);
-	    getnameinfo(&(peername.sa), size, host, sizeof(host),
-		    port, sizeof(port), reverseDNS | NI_NUMERICSERV);
-	    Tcl_DStringAppendElement(dsPtr, host);
-	    Tcl_DStringAppendElement(dsPtr, port);
-	    if (len == 0) {
-		Tcl_DStringEndSublist(dsPtr);
-	    } else {
-		return TCL_OK;
-	    }
-	} else {
 	    /*
-	     * getpeername failed - but if we were asked for all the options
-	     * (len==0), don't flag an error at that point because it could be
-	     * an fconfigure request on a server socket (such sockets have no
-	     * peer). {Copied from unix/tclUnixChan.c}
+	     * It is possible that more than one FD_ACCEPT has been sent, so an extra
+	     * count must be kept. Decrement the count, and reset the readyEvent bit
+	     * if the count is no longer > 0.
 	     */
+	    statePtr->acceptEventCount--;
 
-	    if (len) {
-		TclWinConvertError((DWORD) WSAGetLastError());
-		if (interp) {
-		    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
-			    "can't get peername: %s",
-			    Tcl_PosixError(interp)));
-		}
-		return TCL_ERROR;
+	    if (statePtr->acceptEventCount <= 0) {
+		statePtr->readyEvents &= ~(FD_ACCEPT);
 	    }
-	}
-    }
 
-    if ((len == 0) || ((len > 1) && (optionName[1] == 's') &&
-	    (strncmp(optionName, "-sockname", len) == 0))) {
-	TcpFdList *fds;
-	address sockname;
-	socklen_t size;
-	int found = 0;
+	    SetEvent(tsdPtr->socketListLock);
 
-	if (len == 0) {
-	    Tcl_DStringAppendElement(dsPtr, "-sockname");
-	    Tcl_DStringStartSublist(dsPtr);
+	    /* Caution: TcpAccept() has the side-effect of evaluating the server
+	     * accept script (via AcceptCallbackProc() in tclIOCmd.c), which can
+	     * close the server socket and invalidate statePtr and fds.
+	     * If TcpAccept() accepts a socket we must return immediately and let
+	     * SocketCheckProc queue additional FD_ACCEPT events.
+	     */
+	    TcpAccept(fds, newSocket, addr);
+	    return 1;
 	}
-	for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
-	    sock = fds->fd;
-#ifdef DEBUGGING
-	    fprintf(stderr, "sock == %d\n", sock);
-#endif
-	    size = sizeof(sockname);
-	    if (getsockname(sock, &(sockname.sa), &size) >= 0) {
-		int flags = reverseDNS;
 
-		found = 1;
-		getnameinfo(&sockname.sa, size, host, sizeof(host),
-			NULL, 0, NI_NUMERICHOST);
-		Tcl_DStringAppendElement(dsPtr, host);
+	/* Loop terminated with no sockets accepted; clear the ready mask so 
+	 * we can detect the next connection request. Note that connection 
+	 * requests are level triggered, so if there is a request already 
+	 * pending, a new event will be generated.
+	 */
+	statePtr->acceptEventCount = 0;
+	statePtr->readyEvents &= ~(FD_ACCEPT);
 
-		/*
-		 * We don't want to resolve INADDR_ANY and sin6addr_any; they
-		 * can sometimes cause problems (and never have a name).
-		 */
-		flags |= NI_NUMERICSERV;
-		if (sockname.sa.sa_family == AF_INET) {
-		    if (sockname.sa4.sin_addr.s_addr == INADDR_ANY) {
-			flags |= NI_NUMERICHOST;
-		    }
-		} else if (sockname.sa.sa_family == AF_INET6) {
-		    if ((IN6_ARE_ADDR_EQUAL(&sockname.sa6.sin6_addr,
-				&in6addr_any)) ||
-			    (IN6_IS_ADDR_V4MAPPED(&sockname.sa6.sin6_addr)
-			    && sockname.sa6.sin6_addr.s6_addr[12] == 0
-			    && sockname.sa6.sin6_addr.s6_addr[13] == 0
-			    && sockname.sa6.sin6_addr.s6_addr[14] == 0
-			    && sockname.sa6.sin6_addr.s6_addr[15] == 0)) {
-			flags |= NI_NUMERICHOST;
-		    }
-		}
-		getnameinfo(&sockname.sa, size, host, sizeof(host),
-			port, sizeof(port), flags);
-		Tcl_DStringAppendElement(dsPtr, host);
-		Tcl_DStringAppendElement(dsPtr, port);
-	    }
-	}
-	if (found) {
-	    if (len == 0) {
-		Tcl_DStringEndSublist(dsPtr);
-	    } else {
-		return TCL_OK;
-	    }
-	} else {
-	    if (interp) {
-		TclWinConvertError((DWORD) WSAGetLastError());
-		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
-			"can't get sockname: %s", Tcl_PosixError(interp)));
-	    }
-	    return TCL_ERROR;
-	}
+	SetEvent(tsdPtr->socketListLock);
+	return 1;
     }
 
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
-    if (len == 0 || !strncmp(optionName, "-keepalive", len)) {
-	int optlen;
-	BOOL opt = FALSE;
+    SetEvent(tsdPtr->socketListLock);
 
-	if (len == 0) {
-	    Tcl_DStringAppendElement(dsPtr, "-keepalive");
-	}
-	optlen = sizeof(BOOL);
-	getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, &optlen);
-	if (opt) {
-	    Tcl_DStringAppendElement(dsPtr, "1");
-	} else {
-	    Tcl_DStringAppendElement(dsPtr, "0");
-	}
-	if (len > 0) {
-	    return TCL_OK;
-	}
-    }
+    /*
+     * Mask off unwanted events and compute the read/write mask so we can
+     * notify the channel.
+     */
+
+    events = statePtr->readyEvents & statePtr->watchEvents;
+
+    if (events & FD_CLOSE) {
+	/*
+	 * If the socket was closed and the channel is still interested in
+	 * read events, then we need to ensure that we keep polling for this
+	 * event until someone does something with the channel. Note that we
+	 * do this before calling Tcl_NotifyChannel so we don't have to watch
+	 * out for the channel being deleted out from under us. This may cause
+	 * a redundant trip through the event loop, but it's simpler than
+	 * trying to do unwind protection.
+	 */
+
+	Tcl_Time blockTime = { 0, 0 };
+
+	DEBUG("FD_CLOSE");
+	Tcl_SetMaxBlockTime(&blockTime);
+	mask |= TCL_READABLE|TCL_WRITABLE;
+    } else if (events & FD_READ) {
+	fd_set readFds;
+	struct timeval timeout;
 
-    if (len == 0 || !strncmp(optionName, "-nagle", len)) {
-	int optlen;
-	BOOL opt = FALSE;
+	/*
+	 * We must check to see if data is really available, since someone
+	 * could have consumed the data in the meantime. Turn off async
+	 * notification so select will work correctly. If the socket is still
+	 * readable, notify the channel driver, otherwise reset the async
+	 * select handler and keep waiting.
+	 */
+	DEBUG("FD_READ");
 
-	if (len == 0) {
-	    Tcl_DStringAppendElement(dsPtr, "-nagle");
-	}
-	optlen = sizeof(BOOL);
-	getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, &optlen);
-	if (opt) {
-	    Tcl_DStringAppendElement(dsPtr, "0");
+	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+		(WPARAM) UNSELECT, (LPARAM) statePtr);
+
+	FD_ZERO(&readFds);
+	FD_SET(statePtr->sockets->fd, &readFds);
+	timeout.tv_usec = 0;
+	timeout.tv_sec = 0;
+
+	if (select(0, &readFds, NULL, NULL, &timeout) != 0) {
+	    mask |= TCL_READABLE;
 	} else {
-	    Tcl_DStringAppendElement(dsPtr, "1");
-	}
-	if (len > 0) {
-	    return TCL_OK;
+	    statePtr->readyEvents &= ~(FD_READ);
+	    SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+		    (WPARAM) SELECT, (LPARAM) statePtr);
 	}
     }
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
-
-    if (len > 0) {
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
-	return Tcl_BadChannelOption(interp, optionName,
-		"peername sockname keepalive nagle");
-#else
-	return Tcl_BadChannelOption(interp, optionName, "peername sockname");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+    if (events & FD_WRITE) {
+	DEBUG("FD_WRITE");
+	mask |= TCL_WRITABLE;
     }
-
-    return TCL_OK;
+    if (mask) {
+	DEBUG("Calling Tcl_NotifyChannel...");
+	Tcl_NotifyChannel(statePtr->channel, mask);
+    }
+    DEBUG("returning...");
+    return 1;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TcpWatchProc --
+ * AddSocketInfoFd --
  *
- *	Informs the channel driver of the events that the generic channel code
- *	wishes to receive on this socket.
+ *	This function adds a SOCKET file descriptor to the 'sockets' linked 
+ *	list of a TcpState structure.
  *
  * Results:
  *	None.
  *
  * Side effects:
- *	May cause the notifier to poll if any of the specified conditions are
- *	already true.
+ *	None, except for allocation of memory.
  *
  *----------------------------------------------------------------------
  */
 
 static void
-TcpWatchProc(
-    ClientData instanceData,	/* The socket state. */
-    int mask)			/* Events of interest; an OR-ed combination of
-				 * TCL_READABLE, TCL_WRITABLE and
-				 * TCL_EXCEPTION. */
+AddSocketInfoFd(
+    TcpState *statePtr, 
+    SOCKET socket)
 {
-    SocketInfo *infoPtr = instanceData;
+    TcpFdList *fds = statePtr->sockets;
 
-    DEBUG((mask & TCL_READABLE) ? "+r":"-r");
-    DEBUG((mask & TCL_WRITABLE) ? "+w":"-w");
+    if ( fds == NULL ) {
+	/* Add the first FD */
+	statePtr->sockets = ckalloc(sizeof(TcpFdList));
+	fds = statePtr->sockets;
+    } else {
+	/* Find end of list and append FD */
+	while ( fds->next != NULL ) {
+	    fds = fds->next;
+	}
+    
+	fds->next = ckalloc(sizeof(TcpFdList));
+	fds = fds->next;
+    }
 
-    /*
-     * Update the watch events mask. Only if the socket is not a server
-     * socket. [Bug 557878]
-     */
+    /* Populate new FD */
+    fds->fd = socket;
+    fds->statePtr = statePtr;
+    fds->next = NULL;
+}
 
-    if (!infoPtr->acceptProc) {
-	infoPtr->watchEvents = 0;
-	if (mask & TCL_READABLE) {
-	    infoPtr->watchEvents |= (FD_READ|FD_CLOSE);
-	}
-	if (mask & TCL_WRITABLE) {
-	    infoPtr->watchEvents |= (FD_WRITE|FD_CLOSE);
-	}
+    
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewSocketInfo --
+ *
+ *	This function allocates and initializes a new TcpState structure.
+ *
+ * Results:
+ *	Returns a newly allocated TcpState.
+ *
+ * Side effects:
+ *	None, except for allocation of memory.
+ *
+ *----------------------------------------------------------------------
+ */
 
-	/*
-	 * If there are any conditions already set, then tell the notifier to
-	 * poll rather than block.
-	 */
+static TcpState *
+NewSocketInfo(SOCKET socket)
+{
+    TcpState *statePtr = ckalloc(sizeof(TcpState));
 
-	if (infoPtr->readyEvents & infoPtr->watchEvents) {
-	    Tcl_Time blockTime = { 0, 0 };
+    memset(statePtr, 0, sizeof(TcpState));
 
-	    Tcl_SetMaxBlockTime(&blockTime);
-	}
-    }
+    /*
+     * TIP #218. Removed the code inserting the new structure into the global
+     * list. This is now handled in the thread action callbacks, and only
+     * there.
+     */
+
+    AddSocketInfoFd(statePtr, socket);
+
+    return statePtr;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TcpGetProc --
+ * WaitForSocketEvent --
  *
- *	Called from Tcl_GetChannelHandle to retrieve an OS handle from inside
- *	a TCP socket based channel.
+ *	Waits until one of the specified events occurs on a socket.
  *
  * Results:
- *	Returns TCL_OK with the socket in handlePtr.
+ *	Returns 1 on success or 0 on failure, with an error code in
+ *	errorCodePtr.
  *
  * Side effects:
- *	None.
+ *	Processes socket events off the system queue.
  *
  *----------------------------------------------------------------------
  */
 
 static int
-TcpGetHandleProc(
-    ClientData instanceData,	/* The socket state. */
-    int direction,		/* Not used. */
-    ClientData *handlePtr)	/* Where to store the handle. */
+WaitForSocketEvent(
+    TcpState *statePtr,	/* Information about this socket. */
+    int events,			/* Events to look for. */
+    int *errorCodePtr)		/* Where to store errors? */
 {
-    SocketInfo *statePtr = instanceData;
+    int result = 1;
+    int oldMode;
+    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+    /*
+     * Be sure to disable event servicing so we are truly modal.
+     */
+    DEBUG("=============");
 
-    *handlePtr = INT2PTR(statePtr->sockets->fd);
-    return TCL_OK;
+    oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
+
+    /*
+     * Reset WSAAsyncSelect so we have a fresh set of events pending.
+     */
+
+    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) UNSELECT,
+	    (LPARAM) statePtr);
+    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+	    (LPARAM) statePtr);
+
+    while (1) {
+	if (statePtr->lastError) {
+	    *errorCodePtr = statePtr->lastError;
+	    result = 0;
+	    break;
+	} else if (statePtr->readyEvents & events) {
+	    break;
+	} else if (statePtr->flags & TCP_ASYNC_SOCKET) {
+	    *errorCodePtr = EWOULDBLOCK;
+	    result = 0;
+	    break;
+	}
+
+	/*
+	 * Wait until something happens.
+	 */
+
+	WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
+    }
+
+    (void) Tcl_SetServiceMode(oldMode);
+    return result;
 }
 
 /*
@@ -2844,7 +2967,7 @@ SocketProc(
 {
     int event, error;
     SOCKET socket;
-    SocketInfo *infoPtr = NULL; /* DEBUG */
+    TcpState *statePtr = NULL; /* DEBUG */
     int info_found = 0;
     TcpFdList *fds = NULL;
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
@@ -2901,27 +3024,27 @@ SocketProc(
 	 * eventState flag.
 	 */
 
-	for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
-		infoPtr = infoPtr->nextPtr) {
-    	    DEBUG("Cur InfoPtr");
-    	    if ( FindFDInList(infoPtr,socket) ) {
-    		info_found = 1;
-    		DEBUG("InfoPtr found");
-    		break;
-    	    }
-    	}
-    	/*
-    	 * Check if there is a pending info structure not jet in the
-    	 * list
-    	 */
-    	if ( !info_found
-    		&& tsdPtr->pendingSocketInfo != NULL
-    		&& FindFDInList(tsdPtr->pendingSocketInfo,socket) ) {
-    	    infoPtr = tsdPtr->pendingSocketInfo;
-    	    DEBUG("Pending InfoPtr found");
-    	    info_found = 1;
-    	}
-    	if (info_found) {
+	for (statePtr = tsdPtr->socketList; statePtr != NULL;
+		statePtr = statePtr->nextPtr) {
+	    DEBUG("Cur InfoPtr");
+	    if ( FindFDInList(statePtr,socket) ) {
+		info_found = 1;
+		DEBUG("InfoPtr found");
+		break;
+	    }
+	}
+	/*
+	 * Check if there is a pending info structure not jet in the
+	 * list
+	 */
+	if ( !info_found
+		&& tsdPtr->pendingTcpState != NULL
+		&& FindFDInList(tsdPtr->pendingTcpState,socket) ) {
+	    statePtr = tsdPtr->pendingTcpState;
+	    DEBUG("Pending InfoPtr found");
+	    info_found = 1;
+	}
+	if (info_found) {
 	    if (event & FD_READ) 
 		DEBUG("|->FD_READ");
 	    if (event & FD_WRITE) 
@@ -2937,11 +3060,11 @@ SocketProc(
 
 	    if (event & FD_CLOSE) {
 		DEBUG("FD_CLOSE");
-		infoPtr->acceptEventCount = 0;
-		infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
+		statePtr->acceptEventCount = 0;
+		statePtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
 	    } else if (event & FD_ACCEPT) {
 		DEBUG("FD_ACCEPT");
-		    infoPtr->acceptEventCount++;
+		    statePtr->acceptEventCount++;
 	    }
 
 	    if (event & FD_CONNECT) {
@@ -2952,13 +3075,13 @@ SocketProc(
 		 */
 		if (error != ERROR_SUCCESS) {
 		    TclWinConvertError((DWORD) error);
-		    infoPtr->lastError = Tcl_GetErrno();
+		    statePtr->lastError = Tcl_GetErrno();
 		}
 	    }
 	    /*
 	     * Inform main thread about signaled events
 	     */
-	    infoPtr->readyEvents |= event;
+	    statePtr->readyEvents |= event;
 
 	    /*
 	     * Wake up the Main Thread.
@@ -2971,20 +3094,20 @@ SocketProc(
 
     case SOCKET_SELECT:
 	DEBUG("SOCKET_SELECT");
-	infoPtr = (SocketInfo *) lParam;
-	for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
+	statePtr = (TcpState *) lParam;
+	for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
 #ifdef DEBUGGING
 	    fprintf(stderr,"loop over fd = %d\n",fds->fd);
 #endif
 	    if (wParam == SELECT) {
 		DEBUG("SELECT");
-		if (infoPtr->selectEvents & FD_READ) DEBUG("  READ");
-		if (infoPtr->selectEvents & FD_WRITE) DEBUG("  WRITE");
-		if (infoPtr->selectEvents & FD_CLOSE) DEBUG("  CLOSE");
-		if (infoPtr->selectEvents & FD_CONNECT) DEBUG("  CONNECT");
-		if (infoPtr->selectEvents & FD_ACCEPT) DEBUG("  ACCEPT");
+		if (statePtr->selectEvents & FD_READ) DEBUG("  READ");
+		if (statePtr->selectEvents & FD_WRITE) DEBUG("  WRITE");
+		if (statePtr->selectEvents & FD_CLOSE) DEBUG("  CLOSE");
+		if (statePtr->selectEvents & FD_CONNECT) DEBUG("  CONNECT");
+		if (statePtr->selectEvents & FD_ACCEPT) DEBUG("  ACCEPT");
 		WSAAsyncSelect(fds->fd, hwnd,
-			SOCKET_MESSAGE, infoPtr->selectEvents);
+			SOCKET_MESSAGE, statePtr->selectEvents);
 	    } else {
 		/*
 		 * Clear the selection mask
@@ -3023,11 +3146,11 @@ SocketProc(
 
 static int
 FindFDInList(
-    SocketInfo *infoPtr,
+    TcpState *statePtr,
     SOCKET socket)
 {
     TcpFdList *fds;
-    for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
+    for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
         #ifdef DEBUGGING
 	fprintf(stderr,"socket = %d, fd=%d\n",socket,fds);
 	#endif
@@ -3041,88 +3164,6 @@ FindFDInList(
 /*
  *----------------------------------------------------------------------
  *
- * Tcl_GetHostName --
- *
- *	Returns the name of the local host.
- *
- * Results:
- *	A string containing the network name for this machine. The caller must
- *	not modify or free this string.
- *
- * Side effects:
- *	Caches the name to return for future calls.
- *
- *----------------------------------------------------------------------
- */
-
-const char *
-Tcl_GetHostName(void)
-{
-    return Tcl_GetString(TclGetProcessGlobalValue(&hostName));
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * InitializeHostName --
- *
- *	This routine sets the process global value of the name of the local
- *	host on which the process is running.
- *
- * Results:
- *	None.
- *
- *----------------------------------------------------------------------
- */
-
-void
-InitializeHostName(
-    char **valuePtr,
-    int *lengthPtr,
-    Tcl_Encoding *encodingPtr)
-{
-    TCHAR tbuf[MAX_COMPUTERNAME_LENGTH + 1];
-    DWORD length = MAX_COMPUTERNAME_LENGTH + 1;
-    Tcl_DString ds;
-
-    if (GetComputerName(tbuf, &length) != 0) {
-	/*
-	 * Convert string from native to UTF then change to lowercase.
-	 */
-
-	Tcl_UtfToLower(Tcl_WinTCharToUtf(tbuf, -1, &ds));
-
-    } else {
-	Tcl_DStringInit(&ds);
-	if (TclpHasSockets(NULL) == TCL_OK) {
-	    /*
-	     * The buffer size of 256 is recommended by the MSDN page that
-	     * documents gethostname() as being always adequate.
-	     */
-
-	    Tcl_DString inDs;
-
-	    Tcl_DStringInit(&inDs);
-	    Tcl_DStringSetLength(&inDs, 256);
-	    if (gethostname(Tcl_DStringValue(&inDs),
-		    Tcl_DStringLength(&inDs)) == 0) {
-		Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&inDs), -1,
-			&ds);
-	    }
-	    Tcl_DStringFree(&inDs);
-	}
-    }
-
-    *encodingPtr = Tcl_GetEncoding(NULL, "utf-8");
-    *lengthPtr = Tcl_DStringLength(&ds);
-    *valuePtr = ckalloc((*lengthPtr) + 1);
-    memcpy(*valuePtr, Tcl_DStringValue(&ds), (size_t)(*lengthPtr)+1);
-    Tcl_DStringFree(&ds);
-}
-
-/*
- *----------------------------------------------------------------------
- *
  * TclWinGetSockOpt, et al. --
  *
  *	These functions are wrappers that let us bind the WinSock API
@@ -3239,7 +3280,7 @@ TcpThreadActionProc(
     int action)
 {
     ThreadSpecificData *tsdPtr;
-    SocketInfo *infoPtr = instanceData;
+    TcpState *statePtr = instanceData;
     int notifyCmd;
 
     if (action == TCL_CHANNEL_THREAD_INSERT) {
@@ -3256,19 +3297,19 @@ TcpThreadActionProc(
 
 	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
 	DEBUG("Inserting pointer to list");
-	infoPtr->nextPtr = tsdPtr->socketList;
-	tsdPtr->socketList = infoPtr;
+	statePtr->nextPtr = tsdPtr->socketList;
+	tsdPtr->socketList = statePtr;
 	
-	if (infoPtr == tsdPtr->pendingSocketInfo) {
+	if (statePtr == tsdPtr->pendingTcpState) {
 	    DEBUG("Clearing temporary info pointer");
-	    tsdPtr->pendingSocketInfo = NULL;
+	    tsdPtr->pendingTcpState = NULL;
 	}
 	
 	SetEvent(tsdPtr->socketListLock);
 
 	notifyCmd = SELECT;
     } else {
-	SocketInfo **nextPtrPtr;
+	TcpState **nextPtrPtr;
 	int removed = 0;
 
 	tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -3282,8 +3323,8 @@ TcpThreadActionProc(
 	DEBUG("Removing pointer from list");
 	for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL;
 		nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
-	    if ((*nextPtrPtr) == infoPtr) {
-		(*nextPtrPtr) = infoPtr->nextPtr;
+	    if ((*nextPtrPtr) == statePtr) {
+		(*nextPtrPtr) = statePtr->nextPtr;
 		removed = 1;
 		break;
 	    }
@@ -3309,7 +3350,7 @@ TcpThreadActionProc(
      */
 
     SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
-	    (WPARAM) notifyCmd, (LPARAM) infoPtr);
+	    (WPARAM) notifyCmd, (LPARAM) statePtr);
 }
 
 /*
@@ -3317,5 +3358,7 @@ TcpThreadActionProc(
  * mode: c
  * c-basic-offset: 4
  * fill-column: 78
+ * tab-width: 8
+ * indent-tabs-mode: nil
  * End:
  */
-- 
cgit v0.12