From 5e0d82e5a916c0a59ba7a2827b695d74886edcf3 Mon Sep 17 00:00:00 2001 From: vasiljevic Date: Fri, 10 Mar 2006 14:27:41 +0000 Subject: Finalization of the sockets is now solely done in TclpFinalizeSockets() and not over the thread-exit handler, because the order of actions the Tcl generic core will impose may result in cores/hangs if the thread exit handler tears down socket subsystem too early (See Tcl Bug #1437595). --- mac/tclMacSock.c | 24 +++++----- win/tclWinSock.c | 134 ++++++++++++++++++++++++++++++------------------------- 2 files changed, 83 insertions(+), 75 deletions(-) diff --git a/mac/tclMacSock.c b/mac/tclMacSock.c index f272f87..195182e 100644 --- a/mac/tclMacSock.c +++ b/mac/tclMacSock.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclMacSock.c,v 1.14 2002/04/08 09:03:17 das Exp $ + * RCS: @(#) $Id: tclMacSock.c,v 1.14.2.1 2006/03/10 14:27:41 vasiljevic Exp $ */ #include "tclInt.h" @@ -159,7 +159,6 @@ static void SocketCheckProc _ANSI_ARGS_((ClientData clientData, int flags)); static int SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int flags)); -static void SocketExitHandler _ANSI_ARGS_((ClientData clientData)); static void SocketFreeProc _ANSI_ARGS_((ClientData clientData)); static int SocketReady _ANSI_ARGS_((TcpState *statePtr)); static void SocketSetupProc _ANSI_ARGS_((ClientData clientData, @@ -367,37 +366,36 @@ InitSockets() tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); if (tsdPtr == NULL) { + tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->socketList = NULL; Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL); - Tcl_CreateThreadExitHandler(SocketExitHandler, (ClientData) NULL); } } /* *---------------------------------------------------------------------- * - * SocketExitHandler -- + * TclpFinalizeSockets -- * - * Callback invoked during exit clean up to deinitialize the - * socket module. + * Invoked during exit clean up to deinitialize the socket module. * * Results: * None. * * Side effects: - * None. + * Removed event source. * *---------------------------------------------------------------------- */ -static void -SocketExitHandler( - ClientData clientData) /* Not used. */ +void +TclpFinalizeSockets() { - if (hasSockets) { + ThreadSpecificData *tsdPtr; + + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); + if (tsdPtr != NULL) { Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL); - /* CleanUpExitProc(); - TclMacDeleteExitToShellPatch(CleanUpExitProc); */ } } diff --git a/win/tclWinSock.c b/win/tclWinSock.c index 6643345..99913ea 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWinSock.c,v 1.36.2.4 2005/11/28 09:49:57 dkf Exp $ + * RCS: @(#) $Id: tclWinSock.c,v 1.36.2.5 2006/03/10 14:27:41 vasiljevic Exp $ */ #include "tclWinInt.h" @@ -249,7 +249,6 @@ static LRESULT CALLBACK SocketProc _ANSI_ARGS_((HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)); static Tcl_EventSetupProc SocketSetupProc; -static Tcl_ExitProc SocketThreadExitHandler; static int SocketsEnabled _ANSI_ARGS_((void)); static void TcpAccept _ANSI_ARGS_((SocketInfo *infoPtr)); static Tcl_DriverBlockModeProc TcpBlockProc; @@ -303,7 +302,7 @@ static Tcl_ChannelType tcpChannelType = { * library and set up the winSock function table. If successful, * registers the event window for the socket notifier code. * - * Assumes Mutex is held. + * Assumes socketMutex is held. * * Results: * None. @@ -491,45 +490,41 @@ InitSockets() if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->socketList = NULL; - tsdPtr->hwnd = NULL; - - tsdPtr->threadId = Tcl_GetCurrentThread(); - + tsdPtr->hwnd = NULL; + tsdPtr->threadId = Tcl_GetCurrentThread(); tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (tsdPtr->readyEvent == NULL) { + goto unloadLibrary; + } tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL); + if (tsdPtr->socketListLock == NULL) { + goto unloadLibrary; + } tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread, tsdPtr, 0, &id); - SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST); - if (tsdPtr->socketThread == NULL) { goto unloadLibrary; } - + + SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST); /* - * Wait for the thread to signal that the window has - * been created and is ready to go. Timeout after twenty - * seconds. + * Wait for the thread to signal when the window has + * been created and if it is ready to go. */ - - if (WaitForSingleObject(tsdPtr->readyEvent, 20000) - == WAIT_TIMEOUT) { - goto unloadLibrary; - } + + WaitForSingleObject(tsdPtr->readyEvent, INFINITE); if (tsdPtr->hwnd == NULL) { - goto unloadLibrary; + goto unloadLibrary; /* Trouble creating the window */ } - + Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL); - Tcl_CreateThreadExitHandler(SocketThreadExitHandler, NULL); } return; unloadLibrary: - if (tsdPtr != NULL && tsdPtr->hwnd != NULL) { - SocketThreadExitHandler(0); - } + TclpFinalizeSockets(); FreeLibrary(winSock.hModule); winSock.hModule = NULL; return; @@ -568,7 +563,7 @@ SocketsEnabled() * * SocketExitHandler -- * - * Callback invoked during exit clean up to delete the socket + * Callback invoked during app exit clean up to delete the socket * communication window and to release the WinSock DLL. * * Results: @@ -591,7 +586,7 @@ SocketExitHandler(clientData) * Make sure the socket event handling window is cleaned-up * for, at most, this thread. */ - SocketThreadExitHandler(clientData); + TclpFinalizeSockets(); UnregisterClass("TclSocket", TclWinGetTclInstance()); winSock.WSACleanup(); FreeLibrary(winSock.hModule); @@ -605,49 +600,50 @@ SocketExitHandler(clientData) /* *---------------------------------------------------------------------- * - * SocketThreadExitHandler -- + * TclpFinalizeSockets -- * - * Callback invoked during thread clean up to delete the socket - * event source. + * This function is called from Tcl_FinalizeThread to finalize + * the platform specific socket subsystem. + * Also, it may be called from within this module to cleanup + * the state if unable to initialize the sockets subsystem. * * Results: * None. * * Side effects: - * Delete the event source. + * Deletes the event source and destroys the socket thread. * *---------------------------------------------------------------------- */ - /* ARGSUSED */ -static void -SocketThreadExitHandler(clientData) - ClientData clientData; /* Not used. */ +void +TclpFinalizeSockets() { - ThreadSpecificData *tsdPtr = - (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); - - if (tsdPtr != NULL && tsdPtr->socketThread != NULL) { - DWORD exitCode; - - GetExitCodeThread(tsdPtr->socketThread, &exitCode); - if (exitCode == STILL_ACTIVE) { - PostMessage(tsdPtr->hwnd, SOCKET_TERMINATE, 0, 0); - /* - * Wait for the thread to close. This ensures that we are - * completely cleaned up before we leave this function. - * If Tcl_Finalize was called from DllMain, the thread - * is in a paused state so we need to timeout and continue. - */ + ThreadSpecificData *tsdPtr; - WaitForSingleObject(tsdPtr->socketThread, 100); + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); + if (tsdPtr != NULL) { + if (tsdPtr->socketThread != NULL) { + if (tsdPtr->hwnd != NULL) { + PostMessage(tsdPtr->hwnd, SOCKET_TERMINATE, 0, 0); + /* + * Wait for the thread to exit. This ensures that we are + * completely cleaned up before we leave this function. + */ + WaitForSingleObject(tsdPtr->readyEvent, INFINITE); + tsdPtr->hwnd = NULL; + } + CloseHandle(tsdPtr->socketThread); + tsdPtr->socketThread = NULL; + } + if (tsdPtr->readyEvent != NULL) { + CloseHandle(tsdPtr->readyEvent); + tsdPtr->readyEvent = NULL; + } + if (tsdPtr->socketListLock != NULL) { + CloseHandle(tsdPtr->socketListLock); + tsdPtr->socketListLock = NULL; } - CloseHandle(tsdPtr->socketThread); - tsdPtr->socketThread = NULL; - CloseHandle(tsdPtr->readyEvent); - CloseHandle(tsdPtr->socketListLock); - - Tcl_DeleteThreadExitHandler(SocketThreadExitHandler, NULL); Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL); } } @@ -1033,7 +1029,6 @@ NewSocketInfo(socket) SOCKET socket; { SocketInfo *infoPtr; - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); infoPtr = (SocketInfo *) ckalloc((unsigned) sizeof(SocketInfo)); infoPtr->socket = socket; @@ -2324,28 +2319,43 @@ SocketThread(LPVOID arg) MSG msg; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)(arg); + /* + * Create a dummy window receiving socket events. + */ + tsdPtr->hwnd = CreateWindow("TclSocket", "TclSocket", WS_TILED, 0, 0, 0, 0, NULL, NULL, windowClass.hInstance, arg); /* - * Signal the main thread that the window has been created - * and that the socket thread is ready to go. + * Signalize thread creator that we are done creating the window. */ - + SetEvent(tsdPtr->readyEvent); - + + /* + * If unable to create the window, exit this thread immediately. + */ + if (tsdPtr->hwnd == NULL) { return 1; } /* * Process all messages on the socket window until WM_QUIT. + * This threads exits only when instructed to do so by the + * call to PostMessage(SOCKET_TERMINATE) in TclpFinalizeSockets(). */ while (GetMessage(&msg, NULL, 0, 0) > 0) { DispatchMessage(&msg); } + /* + * This releases waiters on thread exit in TclpFinalizeSockets() + */ + + SetEvent(tsdPtr->readyEvent); + return msg.wParam; } @@ -2703,7 +2713,7 @@ TcpThreadActionProc (instanceData, action) notifyCmd = SELECT; } else { - SocketInfo **nextPtrPtr; + SocketInfo **nextPtrPtr; int removed = 0; tsdPtr = TCL_TSD_INIT(&dataKey); -- cgit v0.12