diff options
author | andreas_kupries <akupries@shaw.ca> | 2005-01-27 00:22:53 (GMT) |
---|---|---|
committer | andreas_kupries <akupries@shaw.ca> | 2005-01-27 00:22:53 (GMT) |
commit | 98dadc4e726ce2e9342141de853d74f6d614cc7b (patch) | |
tree | c0e294ceaa42e69c4fe42ce673e1336cf236d856 /win | |
parent | 3c6ddb9aced9de4312cc2a982bf5715af646eddb (diff) | |
download | tcl-98dadc4e726ce2e9342141de853d74f6d614cc7b.zip tcl-98dadc4e726ce2e9342141de853d74f6d614cc7b.tar.gz tcl-98dadc4e726ce2e9342141de853d74f6d614cc7b.tar.bz2 |
TIP#218 IMPLEMENTATION
* generic/tclDecls.h: Regenerated from tcl.decls.
* generic/tclStubInit.c:
* doc/CrtChannel.3: Documentation of extended API,
* generic/tcl.decls: extended testsuite, and
* generic/tcl.h: implementation. Removal of old
* generic/tclIO.c: driver-specific TclpCut/Splice
* generic/tclInt.h: functions. Replaced with generic
* tests/io.test: thread-action calls through the
* unix/tclUnixChan.c: new hooks. Update of all builtin
* unix/tclUnixPipe.c: channel drivers to version 4.
* unix/tclUnixSock.c: Windows drivers extended to
* win/tclWinChan.c: manage thread state in a thread
* win/tclWinConsole.c: action handler.
* win/tclWinPipe.c:
* win/tclWinSerial.c:
* win/tclWinSock.c:
Diffstat (limited to 'win')
-rw-r--r-- | win/tclWinChan.c | 118 | ||||
-rw-r--r-- | win/tclWinConsole.c | 80 | ||||
-rw-r--r-- | win/tclWinPipe.c | 67 | ||||
-rw-r--r-- | win/tclWinSerial.c | 87 | ||||
-rw-r--r-- | win/tclWinSock.c | 196 |
5 files changed, 318 insertions, 230 deletions
diff --git a/win/tclWinChan.c b/win/tclWinChan.c index fb7a83d..d6ea38c 100644 --- a/win/tclWinChan.c +++ b/win/tclWinChan.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWinChan.c,v 1.38 2005/01/19 22:07:41 mdejong Exp $ + * RCS: @(#) $Id: tclWinChan.c,v 1.39 2005/01/27 00:23:32 andreas_kupries Exp $ */ #include "tclWinInt.h" @@ -97,7 +97,9 @@ static Tcl_WideInt FileWideSeekProc _ANSI_ARGS_((ClientData instanceData, static void FileSetupProc _ANSI_ARGS_((ClientData clientData, int flags)); static void FileWatchProc _ANSI_ARGS_((ClientData instanceData, - int mask)); + int mask)); +static void FileThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); /* * This structure describes the channel type structure for file based IO. @@ -105,7 +107,7 @@ static void FileWatchProc _ANSI_ARGS_((ClientData instanceData, static Tcl_ChannelType fileChannelType = { "file", /* Type name. */ - TCL_CHANNEL_VERSION_3, /* v3 channel */ + TCL_CHANNEL_VERSION_4, /* v4 channel */ FileCloseProc, /* Close proc. */ FileInputProc, /* Input proc. */ FileOutputProc, /* Output proc. */ @@ -119,6 +121,7 @@ static Tcl_ChannelType fileChannelType = { NULL, /* flush proc. */ NULL, /* handler proc. */ FileWideSeekProc, /* Wide seek proc. */ + FileThreadActionProc, /* Thread action proc. */ }; #if defined(HAVE_NO_SEH) && defined(TCL_MEM_DEBUG) @@ -430,11 +433,11 @@ FileCloseProc(instanceData, interp) if (infoPtr == fileInfoPtr) { /* * This channel exists on the thread local list. It should - * have been removed by an earlier call to TclpCutFileChannel, + * have been removed by an earlier Threadaction call, * but do that now since just deallocating fileInfoPtr would * leave an deallocated pointer on the thread local list. */ - TclpCutFileChannel(fileInfoPtr->channel); + FileThreadActionProc(fileInfoPtr,TCL_CHANNEL_THREAD_REMOVE); break; } } @@ -1307,8 +1310,11 @@ TclWinOpenFileChannel(handle, channelName, permissions, appendMode) } infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo)); - infoPtr->nextPtr = tsdPtr->firstFilePtr; - tsdPtr->firstFilePtr = infoPtr; + /* 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. + */ + infoPtr->nextPtr = NULL; infoPtr->validMask = permissions; infoPtr->watchMask = 0; infoPtr->flags = appendMode; @@ -1373,10 +1379,9 @@ TclWinFlushDirtyChannels () /* *---------------------------------------------------------------------- * - * TclpCutFileChannel -- + * FileThreadActionProc -- * - * Remove any thread local refs to this channel. See - * See Tcl_CutChannel for more info. + * Insert or remove any thread local refs to this channel. * * Results: * None. @@ -1387,77 +1392,38 @@ TclWinFlushDirtyChannels () *---------------------------------------------------------------------- */ -void -TclpCutFileChannel(chan) - Tcl_Channel chan; /* The channel being removed. Must - * not be referenced in any - * interpreter. */ +static void +FileThreadActionProc (instanceData, action) + ClientData instanceData; + int action; { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - Channel *chanPtr = (Channel *) chan; - FileInfo *infoPtr; - FileInfo **nextPtrPtr; - int removed = 0; - - if (chanPtr->typePtr != &fileChannelType) { - return; - } - - infoPtr = (FileInfo *) chanPtr->instanceData; + FileInfo *infoPtr = (FileInfo *) instanceData; - for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; - nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { - if ((*nextPtrPtr) == infoPtr) { - (*nextPtrPtr) = infoPtr->nextPtr; - removed = 1; - break; + if (action == TCL_CHANNEL_THREAD_INSERT) { + infoPtr->nextPtr = tsdPtr->firstFilePtr; + tsdPtr->firstFilePtr = infoPtr; + } else { + FileInfo **nextPtrPtr; + int removed = 0; + + for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { + if ((*nextPtrPtr) == infoPtr) { + (*nextPtrPtr) = infoPtr->nextPtr; + removed = 1; + break; + } } - } - - /* - * This could happen if the channel was created in one thread - * and then moved to another without updating the thread - * local data in each thread. - */ - if (!removed) { - Tcl_Panic("file info ptr not on thread channel list"); - } -} - -/* - *---------------------------------------------------------------------- - * - * TclpSpliceFileChannel -- - * - * Insert thread local ref for this channel. - * See Tcl_SpliceChannel for more info. - * - * Results: - * None. - * - * Side effects: - * Changes thread local list of valid channels. - * - *---------------------------------------------------------------------- - */ - -void -TclpSpliceFileChannel(chan) - Tcl_Channel chan; /* The channel being removed. Must - * not be referenced in any - * interpreter. */ -{ - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - Channel *chanPtr = (Channel *) chan; - FileInfo *infoPtr; + /* + * This could happen if the channel was created in one thread + * and then moved to another without updating the thread + * local data in each thread. + */ - if (chanPtr->typePtr != &fileChannelType) { - return; + if (!removed) { + Tcl_Panic("file info ptr not on thread channel list"); + } } - - infoPtr = (FileInfo *) chanPtr->instanceData; - - infoPtr->nextPtr = tsdPtr->firstFilePtr; - tsdPtr->firstFilePtr = infoPtr; } diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index e2dedc4..1648ad7 100644 --- a/win/tclWinConsole.c +++ b/win/tclWinConsole.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWinConsole.c,v 1.12 2004/09/10 01:52:17 davygrvy Exp $ + * RCS: @(#) $Id: tclWinConsole.c,v 1.13 2005/01/27 00:23:33 andreas_kupries Exp $ */ #include "tclWinInt.h" @@ -148,7 +148,7 @@ static int ConsoleEventProc(Tcl_Event *evPtr, int flags); static void ConsoleExitHandler(ClientData clientData); static int ConsoleGetHandleProc(ClientData instanceData, int direction, ClientData *handlePtr); -static ThreadSpecificData *ConsoleInit(void); +static void ConsoleInit(void); static int ConsoleInputProc(ClientData instanceData, char *buf, int toRead, int *errorCode); static int ConsoleOutputProc(ClientData instanceData, @@ -160,6 +160,9 @@ static DWORD WINAPI ConsoleWriterThread(LPVOID arg); static void ProcExitHandler(ClientData clientData); static int WaitForRead(ConsoleInfo *infoPtr, int blocking); +static void ConsoleThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); + /* * This structure describes the channel type structure for command console * based IO. @@ -167,7 +170,7 @@ static int WaitForRead(ConsoleInfo *infoPtr, int blocking); static Tcl_ChannelType consoleChannelType = { "console", /* Type name. */ - TCL_CHANNEL_VERSION_2, /* v2 channel */ + TCL_CHANNEL_VERSION_4, /* v4 channel */ ConsoleCloseProc, /* Close proc. */ ConsoleInputProc, /* Input proc. */ ConsoleOutputProc, /* Output proc. */ @@ -180,6 +183,8 @@ static Tcl_ChannelType consoleChannelType = { ConsoleBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */ + NULL, /* wide seek proc */ + ConsoleThreadActionProc, /* thread action proc */ }; /* @@ -198,7 +203,7 @@ static Tcl_ChannelType consoleChannelType = { *---------------------------------------------------------------------- */ -static ThreadSpecificData * +static void ConsoleInit() { ThreadSpecificData *tsdPtr; @@ -224,7 +229,6 @@ ConsoleInit() Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL); Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL); } - return tsdPtr; } /* @@ -1169,7 +1173,10 @@ ConsoleReaderThread(LPVOID arg) */ Tcl_MutexLock(&consoleMutex); - Tcl_ThreadAlert(infoPtr->threadId); + if (infoPtr->threadId != NULL) { + /* TIP #218. When in flight ignore the event, no one will receive it anyway */ + Tcl_ThreadAlert(infoPtr->threadId); + } Tcl_MutexUnlock(&consoleMutex); } @@ -1255,7 +1262,10 @@ ConsoleWriterThread(LPVOID arg) */ Tcl_MutexLock(&consoleMutex); - Tcl_ThreadAlert(infoPtr->threadId); + if (infoPtr->threadId != NULL) { + /* TIP #218. When in flight ignore the event, no one will receive it anyway */ + Tcl_ThreadAlert(infoPtr->threadId); + } Tcl_MutexUnlock(&consoleMutex); } @@ -1290,10 +1300,9 @@ TclWinOpenConsoleChannel(handle, channelName, permissions) { char encoding[4 + TCL_INTEGER_SPACE]; ConsoleInfo *infoPtr; - ThreadSpecificData *tsdPtr; DWORD id, modes; - tsdPtr = ConsoleInit(); + ConsoleInit(); /* * See if a channel with this handle already exists. @@ -1304,9 +1313,12 @@ TclWinOpenConsoleChannel(handle, channelName, permissions) infoPtr->validMask = permissions; infoPtr->handle = handle; + infoPtr->channel = (Tcl_Channel) NULL; wsprintfA(encoding, "cp%d", GetConsoleCP()); + infoPtr->threadId = Tcl_GetCurrentThread(); + /* * Use the pointer for the name of the result channel. * This keeps the channel names unique, since some may share @@ -1318,8 +1330,6 @@ TclWinOpenConsoleChannel(handle, channelName, permissions) infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName, (ClientData) infoPtr, permissions); - infoPtr->threadId = Tcl_GetCurrentThread(); - if (permissions & TCL_READABLE) { /* * Make sure the console input buffer is ready for only character @@ -1360,3 +1370,51 @@ TclWinOpenConsoleChannel(handle, channelName, permissions) return infoPtr->channel; } + +/* + *---------------------------------------------------------------------- + * + * ConsoleThreadActionProc -- + * + * Insert or remove any thread local refs to this channel. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +static void +ConsoleThreadActionProc (instanceData, action) + ClientData instanceData; + int action; +{ + ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; + + /* We do not access firstConsolePtr in the thread structures. This is + * not for all serials managed by the thread, but only those we are + * watching. Removal of the filevent handlers before transfer thus + * takes care of this structure. + */ + + Tcl_MutexLock(&consoleMutex); + if (action == TCL_CHANNEL_THREAD_INSERT) { + /* We can't copy the thread information from the channel when + * the channel is created. At this time the channel back + * pointer has not been set yet. However in that case the + * threadId has already been set by TclpCreateCommandChannel + * itself, so the structure is still good. + */ + + ConsoleInit (); + if (infoPtr->channel != NULL) { + infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel); + } + } else { + infoPtr->threadId = NULL; + } + Tcl_MutexUnlock(&consoleMutex); +} diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c index 2601e4f..d8a893d 100644 --- a/win/tclWinPipe.c +++ b/win/tclWinPipe.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWinPipe.c,v 1.53 2004/12/01 23:18:55 dgp Exp $ + * RCS: @(#) $Id: tclWinPipe.c,v 1.54 2005/01/27 00:23:34 andreas_kupries Exp $ */ #include "tclWinInt.h" @@ -205,6 +205,9 @@ static void ProcExitHandler(ClientData clientData); static int TempFileName(WCHAR name[MAX_PATH]); static int WaitForRead(PipeInfo *infoPtr, int blocking); +static void PipeThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); + /* * This structure describes the channel type structure for command pipe * based IO. @@ -212,7 +215,7 @@ static int WaitForRead(PipeInfo *infoPtr, int blocking); static Tcl_ChannelType pipeChannelType = { "pipe", /* Type name. */ - TCL_CHANNEL_VERSION_2, /* v2 channel */ + TCL_CHANNEL_VERSION_4, /* v4 channel */ TCL_CLOSE2PROC, /* Close proc. */ PipeInputProc, /* Input proc. */ PipeOutputProc, /* Output proc. */ @@ -225,6 +228,8 @@ static Tcl_ChannelType pipeChannelType = { PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */ + NULL, /* wide seek proc */ + PipeThreadActionProc, /* thread action proc */ }; /* @@ -1696,6 +1701,7 @@ TclpCreateCommandChannel( infoPtr->writeBuf = 0; infoPtr->writeBufLen = 0; infoPtr->writeError = 0; + infoPtr->channel = (Tcl_Channel) NULL; /* * Use one of the fds associated with the channel as the @@ -2977,7 +2983,10 @@ PipeReaderThread(LPVOID arg) */ Tcl_MutexLock(&pipeMutex); - Tcl_ThreadAlert(infoPtr->threadId); + if (infoPtr->threadId != NULL) { + /* TIP #218. When in flight ignore the event, no one will receive it anyway */ + Tcl_ThreadAlert(infoPtr->threadId); + } Tcl_MutexUnlock(&pipeMutex); } @@ -3065,10 +3074,60 @@ PipeWriterThread(LPVOID arg) */ Tcl_MutexLock(&pipeMutex); - Tcl_ThreadAlert(infoPtr->threadId); + if (infoPtr->threadId != NULL) { + /* TIP #218. When in flight ignore the event, no one will receive it anyway */ + Tcl_ThreadAlert(infoPtr->threadId); + } Tcl_MutexUnlock(&pipeMutex); } return 0; } + +/* + *---------------------------------------------------------------------- + * + * PipeThreadActionProc -- + * + * Insert or remove any thread local refs to this channel. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +static void +PipeThreadActionProc (instanceData, action) + ClientData instanceData; + int action; +{ + PipeInfo *infoPtr = (PipeInfo *) instanceData; + + /* We do not access firstPipePtr in the thread structures. This is + * not for all pipes managed by the thread, but only those we are + * watching. Removal of the filevent handlers before transfer thus + * takes care of this structure. + */ + Tcl_MutexLock(&pipeMutex); + if (action == TCL_CHANNEL_THREAD_INSERT) { + /* We can't copy the thread information from the channel when + * the channel is created. At this time the channel back + * pointer has not been set yet. However in that case the + * threadId has already been set by TclpCreateCommandChannel + * itself, so the structure is still good. + */ + + PipeInit (); + if (infoPtr->channel != NULL) { + infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel); + } + } else { + infoPtr->threadId = NULL; + } + Tcl_MutexUnlock(&pipeMutex); +} diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c index 2fa32a1..e06fb8e 100644 --- a/win/tclWinSerial.c +++ b/win/tclWinSerial.c @@ -11,7 +11,7 @@ * * Serial functionality implemented by Rolf.Schroedter@dlr.de * - * RCS: @(#) $Id: tclWinSerial.c,v 1.28 2003/08/19 19:39:56 patthoyts Exp $ + * RCS: @(#) $Id: tclWinSerial.c,v 1.29 2005/01/27 00:23:35 andreas_kupries Exp $ */ #include "tclWinInt.h" @@ -200,6 +200,9 @@ static int SerialSetOptionProc _ANSI_ARGS_(( CONST char *value)); static DWORD WINAPI SerialWriterThread(LPVOID arg); +static void SerialThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); + /* * This structure describes the channel type structure for command serial * based IO. @@ -207,7 +210,7 @@ static DWORD WINAPI SerialWriterThread(LPVOID arg); static Tcl_ChannelType serialChannelType = { "serial", /* Type name. */ - TCL_CHANNEL_VERSION_2, /* v2 channel */ + TCL_CHANNEL_VERSION_4, /* v4 channel */ SerialCloseProc, /* Close proc. */ SerialInputProc, /* Input proc. */ SerialOutputProc, /* Output proc. */ @@ -220,6 +223,8 @@ static Tcl_ChannelType serialChannelType = { SerialBlockProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */ + NULL, /* wide seek proc */ + SerialThreadActionProc, /* thread action proc */ }; /* @@ -1384,7 +1389,10 @@ SerialWriterThread(LPVOID arg) */ Tcl_MutexLock(&serialMutex); - Tcl_ThreadAlert(infoPtr->threadId); + if (infoPtr->threadId != NULL) { + /* TIP #218. When in flight ignore the event, no one will receive it anyway */ + Tcl_ThreadAlert(infoPtr->threadId); + } Tcl_MutexUnlock(&serialMutex); } @@ -1458,16 +1466,25 @@ TclWinOpenSerialChannel(handle, channelName, permissions) int permissions; { SerialInfo *infoPtr; - ThreadSpecificData *tsdPtr; DWORD id; - tsdPtr = SerialInit(); + SerialInit(); infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo)); memset(infoPtr, 0, sizeof(SerialInfo)); - infoPtr->validMask = permissions; - infoPtr->handle = handle; + infoPtr->validMask = permissions; + infoPtr->handle = handle; + infoPtr->channel = (Tcl_Channel) NULL; + infoPtr->readable = 0; + infoPtr->writable = 1; + infoPtr->toWrite = infoPtr->writeQueue = 0; + infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME; + infoPtr->lastEventTime = 0; + infoPtr->lastError = infoPtr->error = 0; + infoPtr->threadId = Tcl_GetCurrentThread(); + infoPtr->sysBufRead = 4096; + infoPtr->sysBufWrite = 4096; /* * Use the pointer to keep the channel names unique, in case @@ -1479,14 +1496,6 @@ TclWinOpenSerialChannel(handle, channelName, permissions) infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName, (ClientData) infoPtr, permissions); - infoPtr->readable = 0; - infoPtr->writable = 1; - infoPtr->toWrite = infoPtr->writeQueue = 0; - infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME; - infoPtr->lastEventTime = 0; - infoPtr->lastError = infoPtr->error = 0; - infoPtr->threadId = Tcl_GetCurrentThread(); - infoPtr->sysBufRead = infoPtr->sysBufWrite = 4096; SetupComm(handle, infoPtr->sysBufRead, infoPtr->sysBufWrite); PurgeComm(handle, @@ -2158,3 +2167,51 @@ SerialGetOptionProc(instanceData, interp, optionName, dsPtr) "mode pollinterval lasterror queue sysbuffer ttystatus xchar"); } } + +/* + *---------------------------------------------------------------------- + * + * SerialThreadActionProc -- + * + * Insert or remove any thread local refs to this channel. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +static void +SerialThreadActionProc (instanceData, action) + ClientData instanceData; + int action; +{ + SerialInfo *infoPtr = (SerialInfo *) instanceData; + + /* We do not access firstSerialPtr in the thread structures. This is + * not for all serials managed by the thread, but only those we are + * watching. Removal of the filevent handlers before transfer thus + * takes care of this structure. + */ + + Tcl_MutexLock(&serialMutex); + if (action == TCL_CHANNEL_THREAD_INSERT) { + /* We can't copy the thread information from the channel when + * the channel is created. At this time the channel back + * pointer has not been set yet. However in that case the + * threadId has already been set by TclpCreateCommandChannel + * itself, so the structure is still good. + */ + + SerialInit (); + if (infoPtr->channel != NULL) { + infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel); + } + } else { + infoPtr->threadId = NULL; + } + Tcl_MutexUnlock(&serialMutex); +} diff --git a/win/tclWinSock.c b/win/tclWinSock.c index c732d36..60c535d 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.44 2004/10/06 14:39:20 dkf Exp $ + * RCS: @(#) $Id: tclWinSock.c,v 1.45 2005/01/27 00:23:35 andreas_kupries Exp $ */ #include "tclWinInt.h" @@ -265,6 +265,10 @@ static int WaitForSocketEvent _ANSI_ARGS_(( int *errorCodePtr)); static DWORD WINAPI SocketThread _ANSI_ARGS_((LPVOID arg)); +static void TcpThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); + + /* * This structure describes the channel type structure for TCP socket * based IO. @@ -272,7 +276,7 @@ static DWORD WINAPI SocketThread _ANSI_ARGS_((LPVOID arg)); static Tcl_ChannelType tcpChannelType = { "tcp", /* Type name. */ - TCL_CHANNEL_VERSION_2, /* v2 channel */ + TCL_CHANNEL_VERSION_4, /* v4 channel */ TcpCloseProc, /* Close proc. */ TcpInputProc, /* Input proc. */ TcpOutputProc, /* Output proc. */ @@ -285,6 +289,8 @@ static Tcl_ChannelType tcpChannelType = { TcpBlockProc, /* Set socket into (non-)blocking mode. */ NULL, /* flush proc. */ NULL, /* handler proc. */ + NULL, /* wide seek proc */ + TcpThreadActionProc, /* thread action proc */ }; @@ -970,7 +976,7 @@ TcpCloseProc(instanceData, interp) Tcl_Interp *interp; /* Unused. */ { SocketInfo *infoPtr = (SocketInfo *) instanceData; - SocketInfo **nextPtrPtr; + /* TIP #218 */ int errorCode = 0; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -995,20 +1001,12 @@ TcpCloseProc(instanceData, interp) } } - /* - * Remove the socket from socketList. + /* TIP #218. Removed the code removing the structure + * from the global socket list. This is now done by + * the thread action callbacks, and only there. This + * happens before this code is called. We can free + * without fear of damaging the list. */ - - WaitForSingleObject(tsdPtr->socketListLock, INFINITE); - for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL; - nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { - if ((*nextPtrPtr) == infoPtr) { - (*nextPtrPtr) = infoPtr->nextPtr; - break; - } - } - - SetEvent(tsdPtr->socketListLock); ckfree((char *) infoPtr); return errorCode; } @@ -1025,7 +1023,7 @@ TcpCloseProc(instanceData, interp) * Returns a newly allocated SocketInfo. * * Side effects: - * Adds the socket to the global socket list. + * None, except for allocation of memory. * *---------------------------------------------------------------------- */ @@ -1049,11 +1047,12 @@ NewSocketInfo(socket) infoPtr->acceptProcData = NULL; infoPtr->lastError = 0; - WaitForSingleObject(tsdPtr->socketListLock, INFINITE); - infoPtr->nextPtr = tsdPtr->socketList; - tsdPtr->socketList = infoPtr; - SetEvent(tsdPtr->socketListLock); - + /* 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. + */ + infoPtr->nextPtr = NULL; + return infoPtr; } @@ -1069,7 +1068,7 @@ NewSocketInfo(socket) * Returns a new SocketInfo, or NULL with an error in interp. * * Side effects: - * Adds a new socket to the socketList. + * None, except for allocation of memory. * *---------------------------------------------------------------------- */ @@ -2665,16 +2664,13 @@ TclWinGetServByName(const char * name, const char * proto) return winSock.getservbyname(name, proto); } - - /* *---------------------------------------------------------------------- * - * TclpCutSockChannel -- + * TcpThreadActionProc -- * - * Remove any thread local refs to this channel. See - * Tcl_CutChannel for more info. + * Insert or remove any thread local refs to this channel. * * Results: * None. @@ -2685,116 +2681,68 @@ TclWinGetServByName(const char * name, const char * proto) *---------------------------------------------------------------------- */ -void -TclpCutSockChannel(chan) - Tcl_Channel chan; /* The channel being removed. Must - * not be referenced in any - * interpreter. */ +static void +TcpThreadActionProc (instanceData, action) + ClientData instanceData; + int action; { ThreadSpecificData *tsdPtr; - SocketInfo *infoPtr; - SocketInfo **nextPtrPtr; - int removed = 0; - - if (Tcl_GetChannelType(chan) != &tcpChannelType) { - return; - } - - /* - * The initializtion of tsdPtr _after_ we have determined that we - * are dealing with socket is necessary. Doing it before causes - * the module to access th tdsPtr when it is not initialized yet, - * causing a lockup. - */ + SocketInfo *infoPtr = (SocketInfo *) instanceData; + int notifyCmd; - tsdPtr = TCL_TSD_INIT(&dataKey); - infoPtr = (SocketInfo *) Tcl_GetChannelInstanceData (chan); + if (action == TCL_CHANNEL_THREAD_INSERT) { + /* + * Ensure that socket subsystem is initialized in this thread, or + * else sockets will not work. + */ - for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL; - nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { - if ((*nextPtrPtr) == infoPtr) { - (*nextPtrPtr) = infoPtr->nextPtr; - removed = 1; - break; - } - } + Tcl_MutexLock(&socketMutex); + InitSockets(); + Tcl_MutexUnlock(&socketMutex); - /* - * This could happen if the channel was created in one thread - * and then moved to another without updating the thread - * local data in each thread. - */ + tsdPtr = TCL_TSD_INIT(&dataKey); - if (!removed) { - Tcl_Panic("file info ptr not on thread channel list"); - } + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); + infoPtr->nextPtr = tsdPtr->socketList; + tsdPtr->socketList = infoPtr; + SetEvent(tsdPtr->socketListLock); - /* - * Stop notifications for the socket to occur in this thread. - */ + notifyCmd = SELECT; + } else { + SocketInfo **nextPtrPtr; + int removed = 0; + + tsdPtr = TCL_TSD_INIT(&dataKey); + + /* TIP #218, Bugfix: All access to socketList has to be protected by the lock */ + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); + for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL; + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { + if ((*nextPtrPtr) == infoPtr) { + (*nextPtrPtr) = infoPtr->nextPtr; + removed = 1; + break; + } + } + SetEvent(tsdPtr->socketListLock); - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) UNSELECT, (LPARAM) infoPtr); -} - -/* - *---------------------------------------------------------------------- - * - * TclpSpliceSockChannel -- - * - * Insert thread local ref for this channel. - * Tcl_SpliceChannel for more info. - * - * Results: - * None. - * - * Side effects: - * Changes thread local list of valid channels. - * - *---------------------------------------------------------------------- - */ + /* + * This could happen if the channel was created in one thread + * and then moved to another without updating the thread + * local data in each thread. + */ -void -TclpSpliceSockChannel(chan) - Tcl_Channel chan; /* The channel being removed. Must - * not be referenced in any - * interpreter. */ -{ - ThreadSpecificData *tsdPtr; - SocketInfo *infoPtr; + if (!removed) { + Tcl_Panic("file info ptr not on thread channel list"); + } - if (Tcl_GetChannelType(chan) != &tcpChannelType) { - return; + notifyCmd = UNSELECT; } /* - * Ensure that socket subsystem is initialized in this thread, or - * else sockets will not work. - */ - - Tcl_MutexLock(&socketMutex); - InitSockets(); - Tcl_MutexUnlock(&socketMutex); - - /* - * The initializtion of tsdPtr _after_ we have determined that we - * are dealing with socket is necessary. Doing it before causes - * the module to access th tdsPtr when it is not initialized yet, - * causing a lockup. - */ - - tsdPtr = TCL_TSD_INIT(&dataKey); - infoPtr = (SocketInfo *) Tcl_GetChannelInstanceData (chan); - - WaitForSingleObject(tsdPtr->socketListLock, INFINITE); - infoPtr->nextPtr = tsdPtr->socketList; - tsdPtr->socketList = infoPtr; - SetEvent(tsdPtr->socketListLock); - - /* - * Ensure that notifications for the socket occur in this thread. + * Ensure that, or stop, notifications for the socket occur in this thread. */ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) SELECT, (LPARAM) infoPtr); + (WPARAM) notifyCmd, (LPARAM) infoPtr); } |