From 2123231f4d20076fce7107118855c3b04308298b Mon Sep 17 00:00:00 2001 From: andreas_kupries Date: Thu, 27 Jan 2005 22:53:23 +0000 Subject: 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: * mac/tclMacChan.c: --- ChangeLog | 23 ++++++ doc/CrtChannel.3 | 71 ++++++++++++++++--- generic/tcl.decls | 20 +++++- generic/tcl.h | 23 +++++- generic/tclDecls.h | 190 +++++++++++++++++++++++++++++++++++++++++++++++++- generic/tclIO.c | 72 +++++++++++++++---- generic/tclInt.h | 4 +- generic/tclStubInit.c | 63 ++++++++++++++++- mac/tclMacChan.c | 117 ++++++++++++------------------- tests/io.test | 8 ++- unix/tclUnixChan.c | 129 ++++++++++++++-------------------- unix/tclUnixPipe.c | 6 +- win/tclWinChan.c | 114 +++++++++++------------------- win/tclWinConsole.c | 80 ++++++++++++++++++--- win/tclWinPipe.c | 67 ++++++++++++++++-- win/tclWinSerial.c | 87 +++++++++++++++++++---- win/tclWinSock.c | 123 ++++++++++++++++++++++++++------ 17 files changed, 891 insertions(+), 306 deletions(-) diff --git a/ChangeLog b/ChangeLog index 001b182..f3bf04d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2005-01-27 Andreas Kupries + + 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: + * mac/tclMacChan.c: + 2005-01-25 Don Porter * library/auto.tcl: Updated [auto_reset] to clear auto-loaded diff --git a/doc/CrtChannel.3 b/doc/CrtChannel.3 index b2360ba..73cea4a 100644 --- a/doc/CrtChannel.3 +++ b/doc/CrtChannel.3 @@ -5,13 +5,13 @@ '\" See the file "license.terms" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -'\" RCS: @(#) $Id: CrtChannel.3,v 1.16.2.2 2004/11/02 09:21:25 dkf Exp $ +'\" RCS: @(#) $Id: CrtChannel.3,v 1.16.2.3 2005/01/27 22:53:27 andreas_kupries Exp $ .so man.macros -.TH Tcl_CreateChannel 3 8.3 Tcl "Tcl Library Procedures" +.TH Tcl_CreateChannel 3 8.4 Tcl "Tcl Library Procedures" .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME -Tcl_CreateChannel, Tcl_GetChannelInstanceData, Tcl_GetChannelType, Tcl_GetChannelName, Tcl_GetChannelHandle, Tcl_GetChannelMode, Tcl_GetChannelBufferSize, Tcl_SetChannelBufferSize, Tcl_NotifyChannel, Tcl_BadChannelOption, Tcl_ChannelName, Tcl_ChannelVersion, Tcl_ChannelBlockModeProc, Tcl_ChannelCloseProc, Tcl_ChannelClose2Proc, Tcl_ChannelInputProc, Tcl_ChannelOutputProc, Tcl_ChannelSeekProc, Tcl_ChannelWideSeekProc, Tcl_ChannelSetOptionProc, Tcl_ChannelGetOptionProc, Tcl_ChannelWatchProc, Tcl_ChannelGetHandleProc, Tcl_ChannelFlushProc, Tcl_ChannelHandlerProc, Tcl_IsChannelShared, Tcl_IsChannelRegistered, Tcl_CutChannel, Tcl_SpliceChannel, Tcl_IsChannelExisting, Tcl_ClearChannelHandlers, Tcl_GetChannelThread, Tcl_ChannelBuffered \- procedures for creating and manipulating channels +Tcl_CreateChannel, Tcl_GetChannelInstanceData, Tcl_GetChannelType, Tcl_GetChannelName, Tcl_GetChannelHandle, Tcl_GetChannelMode, Tcl_GetChannelBufferSize, Tcl_SetChannelBufferSize, Tcl_NotifyChannel, Tcl_BadChannelOption, Tcl_ChannelName, Tcl_ChannelVersion, Tcl_ChannelBlockModeProc, Tcl_ChannelCloseProc, Tcl_ChannelClose2Proc, Tcl_ChannelInputProc, Tcl_ChannelOutputProc, Tcl_ChannelSeekProc, Tcl_ChannelWideSeekProc, Tcl_ChannelSetOptionProc, Tcl_ChannelGetOptionProc, Tcl_ChannelWatchProc, Tcl_ChannelGetHandleProc, Tcl_ChannelFlushProc, Tcl_ChannelHandlerProc, Tcl_ChannelThreadActionProc, Tcl_IsChannelShared, Tcl_IsChannelRegistered, Tcl_CutChannel, Tcl_SpliceChannel, Tcl_IsChannelExisting, Tcl_ClearChannelHandlers, Tcl_GetChannelThread, Tcl_ChannelBuffered \- procedures for creating and manipulating channels .SH SYNOPSIS .nf \fB#include \fR @@ -99,6 +99,9 @@ Tcl_DriverSeekProc * .VS 8.4 Tcl_DriverWideSeekProc * \fBTcl_ChannelWideSeekProc\fR(\fItypePtr\fR) +.sp +Tcl_DriverThreadActionProc * +\fBTcl_ChannelThreadActionProc\fR(\fItypePtr\fR) .VE 8.4 .sp Tcl_DriverSetOptionProc * @@ -289,10 +292,20 @@ name is registered in the (thread)-global list of all channels (result (thread)global list of all channels (of the current thread). Application to a channel still registered in some interpreter is not allowed. +.VS 8.4 +Also notifies the driver if the \fBTcl_ChannelType\fR version is +\fBTCL_CHANNEL_VERSION_4\fR (or higher), and +\fBTcl_DriverThreadActionProc\fR is defined for it. +.VE 8.4 .PP \fBTcl_SpliceChannel\fR adds the specified \fIchannel\fR to the (thread)global list of all channels (of the current thread). Application to a channel registered in some interpreter is not allowed. +.VS 8.4 +Also notifies the driver if the \fBTcl_ChannelType\fR version is +\fBTCL_CHANNEL_VERSION_4\fR (or higher), and +\fBTcl_DriverThreadActionProc\fR is defined for it. +.VE 8.4 .PP \fBTcl_ClearChannelHandlers\fR removes all channelhandlers and event scripts associated with the specified \fIchannel\fR, thus shutting @@ -326,6 +339,7 @@ typedef struct Tcl_ChannelType { Tcl_DriverFlushProc *\fIflushProc\fR; Tcl_DriverHandlerProc *\fIhandlerProc\fR; Tcl_DriverWideSeekProc *\fIwideSeekProc\fR; + Tcl_DriverThreadActionProc *\fIthreadActionProc\fR; } Tcl_ChannelType; .CE .PP @@ -346,6 +360,7 @@ structure, the following functions should be used to obtain the values: \fBTcl_ChannelOutputProc\fR, \fBTcl_ChannelSeekProc\fR, .VS 8.4 \fBTcl_ChannelWideSeekProc\fR, +\fBTcl_ChannelThreadActionProc\fR, .VE 8.4 \fBTcl_ChannelSetOptionProc\fR, \fBTcl_ChannelGetOptionProc\fR, \fBTcl_ChannelWatchProc\fR, \fBTcl_ChannelGetHandleProc\fR, @@ -367,18 +382,27 @@ a pointer to the string. .SH VERSION .PP -The \fIversion\fR field should be set to \fBTCL_CHANNEL_VERSION_2\fR. -If it is not set to this value \fBTCL_CHANNEL_VERSION_3\fR, then this -\fBTcl_ChannelType\fR is assumed to have the older structure. See + +The \fIversion\fR field should be set to the version of the structure +that you require. \fBTCL_CHANNEL_VERSION_2\fR is the minimum recommended. +.VS 8.4 +\fBTCL_CHANNEL_VERSION_3\fR must be set to specifiy the \fIwideSeekProc\fR member. +.VE 8.4 +.VS 8.4 +\fBTCL_CHANNEL_VERSION_4\fR must be set to specifiy the +\fIthreadActionProc\fR member (includes \fIwideSeekProc\fR). +.VE 8.4 +If it is not set to any of these, then this +\fBTcl_ChannelType\fR is assumed to have the original structure. See \fBOLD CHANNEL TYPES\fR for more details. While Tcl will recognize -and function with either structure, stacked channels must be of at +and function with either structures, stacked channels must be of at least \fBTCL_CHANNEL_VERSION_2\fR to function correctly. .PP This value can be retrieved with \fBTcl_ChannelVersion\fR, which returns .VS 8.4 -one of \fBTCL_CHANNEL_VERSION_3\fR, +one of \fBTCL_CHANNEL_VERSION_4\fR, \fBTCL_CHANNEL_VERSION_3\fR, .VE 8.4 -\fBTCL_CHANNEL_VERSION_2\fR or \fBTCL_CHANNEL_VERSION_1\fR. +\fBTCL_CHANNEL_VERSION_2\fR, or \fBTCL_CHANNEL_VERSION_1\fR. .SH BLOCKMODEPROC .PP @@ -790,6 +814,35 @@ type of event occurred on this channel. This value can be retrieved with \fBTcl_ChannelHandlerProc\fR, which returns a pointer to the function. +.VS 8.4 +.SH "THREADACTIONPROC" +.PP +The \fthreadActionProc\fR field contains the address of the function +called by the generic layer when a channel is created, closed, or +going to move to a different thread, i.e. whenever thread-specific +driver state might have to initialized or updated. It can be NULL. +The action \fITCL_CHANNEL_THREAD_REMOVE\fR is used to notify the +driver that it should update or remove any thread-specific data it +might be maintaining for the channel. +.PP +The action \fITCL_CHANNEL_THREAD_INSERT\fR is used to notify the +driver that it should update or initialize any thread-specific data it +might be maintaining using the calling thread as the associate. See +\fBTcl_CutChannel\fR and \fBTcl_SpliceChannel\fR for more detail. +.PP +.CS +typedef void Tcl_DriverThreadActionProc( + ClientData \fIinstanceData\fR, + int \fIaction\fR); +.CE +.PP +\fIInstanceData\fR is the same as the value passed to +\fBTcl_CreateChannel\fR when this channel was created. +.PP +These values can be retrieved with \fBTcl_ChannelThreadActionProc\fR, +which returns a pointer to the function. +.VE 8.4 + .SH TCL_BADCHANNELOPTION .PP This procedure generates a "bad option" error message in an diff --git a/generic/tcl.decls b/generic/tcl.decls index 86151b0..ed95c57 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -11,7 +11,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: tcl.decls,v 1.94.2.2 2003/05/13 09:57:40 mistachkin Exp $ +# RCS: @(#) $Id: tcl.decls,v 1.94.2.3 2005/01/27 22:53:28 andreas_kupries Exp $ library tcl @@ -1754,6 +1754,24 @@ declare 493 generic { Tcl_ChannelType *chanTypePtr) } +# Slots 494 to 553 are taken already by 8.5 +# #111 - Dicts (494 ... 504) +# #59 - Config (505) +# #139 - Namespace API (506 ... 517) +# #137 - source -encoding (518) +# #121 - ExitProc (519) +# #121 - Resource Limits (520 ... 534) +# #226 - S/R Interp State (535 ... 537) +# #227 - S/G Return Opts (538 ... 539) +# #235 - Ensemble C API (540 ... 551) +# #233 - Virtualized Time (552 ... 553) + +# TIP#218 (Driver Thread Actions) davygrvy/akupries ChannelType ver 4 +# These slots are used by 8.5 as well. +declare 554 generic { + Tcl_DriverThreadActionProc *Tcl_ChannelThreadActionProc(Tcl_ChannelType *chanTypePtr) +} + ############################################################################## # Define the platform specific public Tcl interface. These functions are diff --git a/generic/tcl.h b/generic/tcl.h index 75418e3..242e352 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -13,7 +13,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tcl.h,v 1.153.2.16 2004/11/25 00:19:27 hobbs Exp $ + * RCS: @(#) $Id: tcl.h,v 1.153.2.17 2005/01/27 22:53:29 andreas_kupries Exp $ */ #ifndef _TCL @@ -1425,6 +1425,14 @@ typedef int (Tcl_WaitForEventProc) _ANSI_ARGS_((Tcl_Time *timePtr)); #define TCL_CHANNEL_VERSION_1 ((Tcl_ChannelTypeVersion) 0x1) #define TCL_CHANNEL_VERSION_2 ((Tcl_ChannelTypeVersion) 0x2) #define TCL_CHANNEL_VERSION_3 ((Tcl_ChannelTypeVersion) 0x3) +#define TCL_CHANNEL_VERSION_4 ((Tcl_ChannelTypeVersion) 0x4) + +/* + * TIP #218: Channel Actions, Ids for Tcl_DriverThreadActionProc + */ + +#define TCL_CHANNEL_THREAD_INSERT (0) +#define TCL_CHANNEL_THREAD_REMOVE (1) /* * Typedefs for the various operations in a channel type: @@ -1460,6 +1468,9 @@ typedef Tcl_WideInt (Tcl_DriverWideSeekProc) _ANSI_ARGS_(( ClientData instanceData, Tcl_WideInt offset, int mode, int *errorCodePtr)); + /* TIP #218, Channel Thread Actions */ +typedef void (Tcl_DriverThreadActionProc) _ANSI_ARGS_ (( + ClientData instanceData, int action)); /* * The following declarations either map ckalloc and ckfree to @@ -1550,6 +1561,16 @@ typedef struct Tcl_ChannelType { * handle 64-bit offsets. May be * NULL, and must be NULL if * seekProc is NULL. */ + + /* + * Only valid in TCL_CHANNEL_VERSION_4 channels or later + * TIP #218, Channel Thread Actions + */ + Tcl_DriverThreadActionProc *threadActionProc; + /* Procedure to call to notify + * the driver of thread specific + * activity for a channel. + * May be NULL. */ } Tcl_ChannelType; /* diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 7cb8616..8ecbe80 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -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: tclDecls.h,v 1.93.2.5 2004/06/10 17:17:42 andreas_kupries Exp $ + * RCS: @(#) $Id: tclDecls.h,v 1.93.2.6 2005/01/27 22:53:30 andreas_kupries Exp $ */ #ifndef _TCLDECLS @@ -1564,6 +1564,69 @@ EXTERN Tcl_WideInt Tcl_Tell _ANSI_ARGS_((Tcl_Channel chan)); /* 493 */ EXTERN Tcl_DriverWideSeekProc * Tcl_ChannelWideSeekProc _ANSI_ARGS_(( Tcl_ChannelType * chanTypePtr)); +/* Slot 494 is reserved */ +/* Slot 495 is reserved */ +/* Slot 496 is reserved */ +/* Slot 497 is reserved */ +/* Slot 498 is reserved */ +/* Slot 499 is reserved */ +/* Slot 500 is reserved */ +/* Slot 501 is reserved */ +/* Slot 502 is reserved */ +/* Slot 503 is reserved */ +/* Slot 504 is reserved */ +/* Slot 505 is reserved */ +/* Slot 506 is reserved */ +/* Slot 507 is reserved */ +/* Slot 508 is reserved */ +/* Slot 509 is reserved */ +/* Slot 510 is reserved */ +/* Slot 511 is reserved */ +/* Slot 512 is reserved */ +/* Slot 513 is reserved */ +/* Slot 514 is reserved */ +/* Slot 515 is reserved */ +/* Slot 516 is reserved */ +/* Slot 517 is reserved */ +/* Slot 518 is reserved */ +/* Slot 519 is reserved */ +/* Slot 520 is reserved */ +/* Slot 521 is reserved */ +/* Slot 522 is reserved */ +/* Slot 523 is reserved */ +/* Slot 524 is reserved */ +/* Slot 525 is reserved */ +/* Slot 526 is reserved */ +/* Slot 527 is reserved */ +/* Slot 528 is reserved */ +/* Slot 529 is reserved */ +/* Slot 530 is reserved */ +/* Slot 531 is reserved */ +/* Slot 532 is reserved */ +/* Slot 533 is reserved */ +/* Slot 534 is reserved */ +/* Slot 535 is reserved */ +/* Slot 536 is reserved */ +/* Slot 537 is reserved */ +/* Slot 538 is reserved */ +/* Slot 539 is reserved */ +/* Slot 540 is reserved */ +/* Slot 541 is reserved */ +/* Slot 542 is reserved */ +/* Slot 543 is reserved */ +/* Slot 544 is reserved */ +/* Slot 545 is reserved */ +/* Slot 546 is reserved */ +/* Slot 547 is reserved */ +/* Slot 548 is reserved */ +/* Slot 549 is reserved */ +/* Slot 550 is reserved */ +/* Slot 551 is reserved */ +/* Slot 552 is reserved */ +/* Slot 553 is reserved */ +/* 554 */ +EXTERN Tcl_DriverThreadActionProc * Tcl_ChannelThreadActionProc _ANSI_ARGS_(( + Tcl_ChannelType * chanTypePtr)); typedef struct TclStubHooks { struct TclPlatStubs *tclPlatStubs; @@ -2117,6 +2180,67 @@ typedef struct TclStubs { Tcl_WideInt (*tcl_Seek) _ANSI_ARGS_((Tcl_Channel chan, Tcl_WideInt offset, int mode)); /* 491 */ Tcl_WideInt (*tcl_Tell) _ANSI_ARGS_((Tcl_Channel chan)); /* 492 */ Tcl_DriverWideSeekProc * (*tcl_ChannelWideSeekProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 493 */ + void *reserved494; + void *reserved495; + void *reserved496; + void *reserved497; + void *reserved498; + void *reserved499; + void *reserved500; + void *reserved501; + void *reserved502; + void *reserved503; + void *reserved504; + void *reserved505; + void *reserved506; + void *reserved507; + void *reserved508; + void *reserved509; + void *reserved510; + void *reserved511; + void *reserved512; + void *reserved513; + void *reserved514; + void *reserved515; + void *reserved516; + void *reserved517; + void *reserved518; + void *reserved519; + void *reserved520; + void *reserved521; + void *reserved522; + void *reserved523; + void *reserved524; + void *reserved525; + void *reserved526; + void *reserved527; + void *reserved528; + void *reserved529; + void *reserved530; + void *reserved531; + void *reserved532; + void *reserved533; + void *reserved534; + void *reserved535; + void *reserved536; + void *reserved537; + void *reserved538; + void *reserved539; + void *reserved540; + void *reserved541; + void *reserved542; + void *reserved543; + void *reserved544; + void *reserved545; + void *reserved546; + void *reserved547; + void *reserved548; + void *reserved549; + void *reserved550; + void *reserved551; + void *reserved552; + void *reserved553; + Tcl_DriverThreadActionProc * (*tcl_ChannelThreadActionProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 554 */ } TclStubs; #ifdef __cplusplus @@ -4133,6 +4257,70 @@ extern TclStubs *tclStubsPtr; #define Tcl_ChannelWideSeekProc \ (tclStubsPtr->tcl_ChannelWideSeekProc) /* 493 */ #endif +/* Slot 494 is reserved */ +/* Slot 495 is reserved */ +/* Slot 496 is reserved */ +/* Slot 497 is reserved */ +/* Slot 498 is reserved */ +/* Slot 499 is reserved */ +/* Slot 500 is reserved */ +/* Slot 501 is reserved */ +/* Slot 502 is reserved */ +/* Slot 503 is reserved */ +/* Slot 504 is reserved */ +/* Slot 505 is reserved */ +/* Slot 506 is reserved */ +/* Slot 507 is reserved */ +/* Slot 508 is reserved */ +/* Slot 509 is reserved */ +/* Slot 510 is reserved */ +/* Slot 511 is reserved */ +/* Slot 512 is reserved */ +/* Slot 513 is reserved */ +/* Slot 514 is reserved */ +/* Slot 515 is reserved */ +/* Slot 516 is reserved */ +/* Slot 517 is reserved */ +/* Slot 518 is reserved */ +/* Slot 519 is reserved */ +/* Slot 520 is reserved */ +/* Slot 521 is reserved */ +/* Slot 522 is reserved */ +/* Slot 523 is reserved */ +/* Slot 524 is reserved */ +/* Slot 525 is reserved */ +/* Slot 526 is reserved */ +/* Slot 527 is reserved */ +/* Slot 528 is reserved */ +/* Slot 529 is reserved */ +/* Slot 530 is reserved */ +/* Slot 531 is reserved */ +/* Slot 532 is reserved */ +/* Slot 533 is reserved */ +/* Slot 534 is reserved */ +/* Slot 535 is reserved */ +/* Slot 536 is reserved */ +/* Slot 537 is reserved */ +/* Slot 538 is reserved */ +/* Slot 539 is reserved */ +/* Slot 540 is reserved */ +/* Slot 541 is reserved */ +/* Slot 542 is reserved */ +/* Slot 543 is reserved */ +/* Slot 544 is reserved */ +/* Slot 545 is reserved */ +/* Slot 546 is reserved */ +/* Slot 547 is reserved */ +/* Slot 548 is reserved */ +/* Slot 549 is reserved */ +/* Slot 550 is reserved */ +/* Slot 551 is reserved */ +/* Slot 552 is reserved */ +/* Slot 553 is reserved */ +#ifndef Tcl_ChannelThreadActionProc +#define Tcl_ChannelThreadActionProc \ + (tclStubsPtr->tcl_ChannelThreadActionProc) /* 554 */ +#endif #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */ diff --git a/generic/tclIO.c b/generic/tclIO.c index 054cc89..90451c1 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIO.c,v 1.61.2.8 2004/09/10 20:06:41 dkf Exp $ + * RCS: @(#) $Id: tclIO.c,v 1.61.2.9 2005/01/27 22:53:32 andreas_kupries Exp $ */ #include "tclInt.h" @@ -1208,18 +1208,19 @@ Tcl_CreateChannel(typePtr, chanName, instanceData, mask) * in the list on exit. * * JH: Could call Tcl_SpliceChannel, but need to avoid NULL check. + * + * TIP #218. + * AK: Just initialize the field to NULL before invoking Tcl_SpliceChannel + * We need Tcl_SpliceChannel, for the threadAction calls. + * There is no real reason to duplicate all of this. + * NOTE: All drivers using thread actions now have to perform their TSD + * manipulation only in their thread action proc. Doing it when + * creating their instance structures will collide with the thread + * action activity and lead to damaged lists. */ - statePtr->nextCSPtr = tsdPtr->firstCSPtr; - tsdPtr->firstCSPtr = statePtr; - - /* - * TIP #10. Mark the current thread as the one managing the new - * channel. Note: 'Tcl_GetCurrentThread' returns sensible - * values even for a non-threaded core. - */ - - statePtr->managingThread = Tcl_GetCurrentThread (); + statePtr->nextCSPtr = (ChannelState *) NULL; + Tcl_SpliceChannel ((Tcl_Channel) chanPtr); /* * Install this channel in the first empty standard channel slot, if @@ -2378,7 +2379,7 @@ CloseChannel(interp, chanPtr, errorCode) * Resets the field 'nextCSPtr' of the specified channel state to NULL. * * NOTE: - * The channel to splice out of the list must not be referenced + * The channel to cut out of the list must not be referenced * in any interpreter. This is something this procedure cannot * check (despite the refcount) because the caller usually wants * fiddle with the channel (like transfering it to a different @@ -2400,6 +2401,7 @@ Tcl_CutChannel(chan) * channel out of the list on close. */ ChannelState *statePtr = ((Channel *) chan)->state; /* state of the channel stack. */ + Tcl_DriverThreadActionProc *threadActionProc; /* * Remove this channel from of the list of all channels @@ -2422,7 +2424,12 @@ Tcl_CutChannel(chan) statePtr->nextCSPtr = (ChannelState *) NULL; - TclpCutFileChannel(chan); + /* TIP #218, Channel Thread Actions */ + threadActionProc = Tcl_ChannelThreadActionProc (Tcl_GetChannelType (chan)); + if (threadActionProc != NULL) { + (*threadActionProc) (Tcl_GetChannelInstanceData(chan), + TCL_CHANNEL_THREAD_REMOVE); + } } /* @@ -2441,7 +2448,7 @@ Tcl_CutChannel(chan) * Nothing. * * NOTE: - * The channel to add to the list must not be referenced in any + * The channel to splice into the list must not be referenced in any * interpreter. This is something this procedure cannot check * (despite the refcount) because the caller usually wants figgle * with the channel (like transfering it to a different thread) @@ -2459,6 +2466,7 @@ Tcl_SpliceChannel(chan) { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); ChannelState *statePtr = ((Channel *) chan)->state; + Tcl_DriverThreadActionProc *threadActionProc; if (statePtr->nextCSPtr != (ChannelState *) NULL) { panic("Tcl_SpliceChannel: trying to add channel used in different list"); @@ -2475,7 +2483,12 @@ Tcl_SpliceChannel(chan) statePtr->managingThread = Tcl_GetCurrentThread (); - TclpSpliceFileChannel(chan); + /* TIP #218, Channel Thread Actions */ + threadActionProc = Tcl_ChannelThreadActionProc (Tcl_GetChannelType (chan)); + if (threadActionProc != NULL) { + (*threadActionProc) (Tcl_GetChannelInstanceData(chan), + TCL_CHANNEL_THREAD_INSERT); + } } /* @@ -8902,6 +8915,8 @@ Tcl_ChannelVersion(chanTypePtr) return TCL_CHANNEL_VERSION_2; } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_3) { return TCL_CHANNEL_VERSION_3; + } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_4) { + return TCL_CHANNEL_VERSION_4; } else { /* * In threadActionProc; + } else { + return NULL; + } +} #if 0 /* For future debugging work, a simple function to print the flags of diff --git a/generic/tclInt.h b/generic/tclInt.h index c3c0904..81d1304 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInt.h,v 1.118.2.7 2004/07/21 01:30:57 hobbs Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.118.2.8 2005/01/27 22:53:33 andreas_kupries Exp $ */ #ifndef _TCLINT @@ -1749,8 +1749,6 @@ EXTERN int TclpObjStat _ANSI_ARGS_((Tcl_Obj *pathPtr, Tcl_StatBuf *buf)); EXTERN Tcl_Channel TclpOpenFileChannel _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *pathPtr, int mode, int permissions)); -EXTERN void TclpCutFileChannel _ANSI_ARGS_((Tcl_Channel chan)); -EXTERN void TclpSpliceFileChannel _ANSI_ARGS_((Tcl_Channel chan)); EXTERN void TclpPanic _ANSI_ARGS_(TCL_VARARGS(CONST char *, format)); EXTERN char * TclpReadlink _ANSI_ARGS_((CONST char *fileName, diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index d054577..b61bf2c 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.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: tclStubInit.c,v 1.79.2.8 2004/10/14 15:30:52 dkf Exp $ + * RCS: @(#) $Id: tclStubInit.c,v 1.79.2.9 2005/01/27 22:53:34 andreas_kupries Exp $ */ #include "tclInt.h" @@ -953,6 +953,67 @@ TclStubs tclStubs = { Tcl_Seek, /* 491 */ Tcl_Tell, /* 492 */ Tcl_ChannelWideSeekProc, /* 493 */ + NULL, /* 494 */ + NULL, /* 495 */ + NULL, /* 496 */ + NULL, /* 497 */ + NULL, /* 498 */ + NULL, /* 499 */ + NULL, /* 500 */ + NULL, /* 501 */ + NULL, /* 502 */ + NULL, /* 503 */ + NULL, /* 504 */ + NULL, /* 505 */ + NULL, /* 506 */ + NULL, /* 507 */ + NULL, /* 508 */ + NULL, /* 509 */ + NULL, /* 510 */ + NULL, /* 511 */ + NULL, /* 512 */ + NULL, /* 513 */ + NULL, /* 514 */ + NULL, /* 515 */ + NULL, /* 516 */ + NULL, /* 517 */ + NULL, /* 518 */ + NULL, /* 519 */ + NULL, /* 520 */ + NULL, /* 521 */ + NULL, /* 522 */ + NULL, /* 523 */ + NULL, /* 524 */ + NULL, /* 525 */ + NULL, /* 526 */ + NULL, /* 527 */ + NULL, /* 528 */ + NULL, /* 529 */ + NULL, /* 530 */ + NULL, /* 531 */ + NULL, /* 532 */ + NULL, /* 533 */ + NULL, /* 534 */ + NULL, /* 535 */ + NULL, /* 536 */ + NULL, /* 537 */ + NULL, /* 538 */ + NULL, /* 539 */ + NULL, /* 540 */ + NULL, /* 541 */ + NULL, /* 542 */ + NULL, /* 543 */ + NULL, /* 544 */ + NULL, /* 545 */ + NULL, /* 546 */ + NULL, /* 547 */ + NULL, /* 548 */ + NULL, /* 549 */ + NULL, /* 550 */ + NULL, /* 551 */ + NULL, /* 552 */ + NULL, /* 553 */ + Tcl_ChannelThreadActionProc, /* 554 */ }; /* !END!: Do not edit above this line. */ diff --git a/mac/tclMacChan.c b/mac/tclMacChan.c index 555e333..4c40c01 100644 --- a/mac/tclMacChan.c +++ b/mac/tclMacChan.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: tclMacChan.c,v 1.21 2003/03/03 20:22:42 das Exp $ + * RCS: @(#) $Id: tclMacChan.c,v 1.21.2.1 2005/01/27 22:53:34 andreas_kupries Exp $ */ #include "tclInt.h" @@ -102,6 +102,8 @@ static int FileSeek _ANSI_ARGS_((ClientData instanceData, long offset, int mode, int *errorCode)); static void FileSetupProc _ANSI_ARGS_((ClientData clientData, int flags)); +static void FileThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); static Tcl_Channel OpenFileChannel _ANSI_ARGS_((CONST char *fileName, int mode, int permissions, int *errorCodePtr)); static int StdIOBlockMode _ANSI_ARGS_((ClientData instanceData, @@ -123,7 +125,7 @@ static int StdReady _ANSI_ARGS_((ClientData instanceData, static Tcl_ChannelType consoleChannelType = { "file", /* Type name. */ - (Tcl_ChannelTypeVersion)StdIOBlockMode, /* Set blocking/nonblocking mode.*/ + TCL_CHANNEL_VERSION_4, /* v4 channel */ StdIOClose, /* Close proc. */ StdIOInput, /* Input proc. */ StdIOOutput, /* Output proc. */ @@ -132,6 +134,12 @@ static Tcl_ChannelType consoleChannelType = { NULL, /* Get option proc. */ CommonWatch, /* Initialize notifier. */ CommonGetHandle /* Get OS handles out of channel. */ + NULL, /* close2proc. */ + StdIOBlockMode, /* Set blocking/nonblocking mode.*/ + NULL, /* flush proc. */ + NULL, /* handler proc. */ + NULL, /* wide seek proc. */ + NULL, /* thread actions */ }; /* @@ -140,8 +148,7 @@ static Tcl_ChannelType consoleChannelType = { static Tcl_ChannelType fileChannelType = { "file", /* Type name. */ - (Tcl_ChannelTypeVersion)FileBlockMode, /* Set blocking or - * non-blocking mode.*/ + TCL_CHANNEL_VERSION_4, /* v4 channel */ FileClose, /* Close proc. */ FileInput, /* Input proc. */ FileOutput, /* Output proc. */ @@ -150,6 +157,12 @@ static Tcl_ChannelType fileChannelType = { NULL, /* Get option proc. */ CommonWatch, /* Initialize notifier. */ CommonGetHandle /* Get OS handles out of channel. */ + NULL, /* close2proc. */ + FileBlockMode, /* Set blocking/nonblocking mode.*/ + NULL, /* flush proc. */ + NULL, /* handler proc. */ + NULL, /* wide seek proc. */ + FileThreadActionProc, /* thread actions */ }; @@ -1212,10 +1225,9 @@ CommonWatch( /* *---------------------------------------------------------------------- * - * TclpCutFileChannel -- + * FileThreadActionProc -- * - * 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. @@ -1226,75 +1238,38 @@ CommonWatch( *---------------------------------------------------------------------- */ -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; - FileState *infoPtr; - FileState **nextPtrPtr; - int removed = 0; + FileState *infoPtr = (FileState *) instanceData; - if (chanPtr->typePtr != &fileChannelType) - return; + if (action == TCL_CHANNEL_THREAD_INSERT) { + infoPtr->nextPtr = tsdPtr->firstFilePtr; + tsdPtr->firstFilePtr = infoPtr; + } else { + FileState **nextPtrPtr; + int removed = 0; + + for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { + if ((*nextPtrPtr) == infoPtr) { + (*nextPtrPtr) = infoPtr->nextPtr; + removed = 1; + break; + } + } - infoPtr = (FileState *) chanPtr->instanceData; + /* + * 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. + */ - for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; - nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { - if ((*nextPtrPtr) == infoPtr) { - (*nextPtrPtr) = infoPtr->nextPtr; - removed = 1; - break; + if (!removed) { + panic("file info ptr not on thread channel list"); } } - - /* - * 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) - panic("file info ptr not on thread channel list"); - -} - -/* - *---------------------------------------------------------------------- - * - * TclpSpliceFileChannel -- - * - * Insert thread local ref for this channel. - * 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; - FileState *infoPtr; - - if (chanPtr->typePtr != &fileChannelType) - return; - - infoPtr = (FileState *) chanPtr->instanceData; - - infoPtr->nextPtr = tsdPtr->firstFilePtr; - tsdPtr->firstFilePtr = infoPtr; } diff --git a/tests/io.test b/tests/io.test index 9492b22..ce9fce1 100644 --- a/tests/io.test +++ b/tests/io.test @@ -12,7 +12,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: io.test,v 1.40.2.8 2004/11/11 01:16:20 das Exp $ +# RCS: @(#) $Id: io.test,v 1.40.2.9 2005/01/27 22:53:34 andreas_kupries Exp $ if {[catch {package require tcltest 2}]} { puts stderr "Skipping tests in [info script]. tcltest 2 required." @@ -1712,6 +1712,12 @@ test io-14.9 {reuse of stdio special channels} {stdio openpipe fileevent} { set f [open "|[list [interpreter] $path(script) [array get path]]" r] set c [gets $f] close $f + # Added delay to give Windows time to stop the spawned process and clean + # up its grip on the file test1. Added delete as proper test cleanup. + # The failing tests were 18.1 and 18.2 as first re-users of file "test1". + after 10000 + file delete $path(script) + file delete $path(test1) set c } hello diff --git a/unix/tclUnixChan.c b/unix/tclUnixChan.c index b875e08..b25d23f 100644 --- a/unix/tclUnixChan.c +++ b/unix/tclUnixChan.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixChan.c,v 1.42.2.4 2004/11/17 02:52:25 hobbs Exp $ + * RCS: @(#) $Id: tclUnixChan.c,v 1.42.2.5 2005/01/27 22:53:35 andreas_kupries Exp $ */ #include "tclInt.h" /* Internal definitions for Tcl. */ @@ -250,6 +250,10 @@ static int FileOutputProc _ANSI_ARGS_(( int toWrite, int *errorCode)); static int FileSeekProc _ANSI_ARGS_((ClientData instanceData, long offset, int mode, int *errorCode)); +#ifdef DEPRECATED +static void FileThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); +#endif static Tcl_WideInt FileWideSeekProc _ANSI_ARGS_((ClientData instanceData, Tcl_WideInt offset, int mode, int *errorCode)); static void FileWatchProc _ANSI_ARGS_((ClientData instanceData, @@ -305,7 +309,7 @@ static Tcl_Channel MakeTcpClientChannelMode _ANSI_ARGS_( 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. */ @@ -319,6 +323,11 @@ static Tcl_ChannelType fileChannelType = { NULL, /* flush proc. */ NULL, /* handler proc. */ FileWideSeekProc, /* wide seek proc. */ +#ifdef DEPRECATED + FileThreadActionProc, /* thread actions */ +#else + NULL, +#endif }; #ifdef SUPPORTS_TTY @@ -329,7 +338,7 @@ static Tcl_ChannelType fileChannelType = { static Tcl_ChannelType ttyChannelType = { "tty", /* Type name. */ - TCL_CHANNEL_VERSION_2, /* v2 channel */ + TCL_CHANNEL_VERSION_4, /* v4 channel */ TtyCloseProc, /* Close proc. */ FileInputProc, /* Input proc. */ #if BAD_TIP35_FLUSH @@ -346,6 +355,8 @@ static Tcl_ChannelType ttyChannelType = { FileBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */ + NULL, /* wide seek proc. */ + NULL, /* thread action proc. */ }; #endif /* SUPPORTS_TTY */ @@ -356,7 +367,7 @@ static Tcl_ChannelType ttyChannelType = { 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. */ @@ -369,6 +380,8 @@ static Tcl_ChannelType tcpChannelType = { TcpBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */ + NULL, /* wide seek proc. */ + NULL, /* thread action proc. */ }; @@ -1840,8 +1853,11 @@ TclpOpenFileChannel(interp, pathPtr, mode, permissions) #ifdef DEPRECATED if (channelTypePtr == &fileChannelType) { - fsPtr->nextPtr = tsdPtr->firstFilePtr; - tsdPtr->firstFilePtr = fsPtr; + /* 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. + */ + fsPtr->nextPtr = NULL; } #endif /* DEPRECATED */ fsPtr->validMask = channelPermissions | TCL_EXCEPTION; @@ -3281,13 +3297,13 @@ TclUnixWaitForFile(fd, mask, timeout) return result; } +#ifdef DEPRECATED /* *---------------------------------------------------------------------- * - * TclpCutFileChannel -- + * FileThreadActionProc -- * - * 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. @@ -3298,79 +3314,40 @@ TclUnixWaitForFile(fd, mask, timeout) *---------------------------------------------------------------------- */ -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; { -#ifdef DEPRECATED ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - Channel *chanPtr = (Channel *) chan; - FileState *fsPtr; - FileState **nextPtrPtr; - int removed = 0; + FileState *fsPtr = (FileState *) instanceData; - if (chanPtr->typePtr != &fileChannelType) - return; + if (action == TCL_CHANNEL_THREAD_INSERT) { + fsPtr->nextPtr = tsdPtr->firstFilePtr; + tsdPtr->firstFilePtr = fsPtr; + } else { + FileState **nextPtrPtr; + int removed = 0; + + for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { + if ((*nextPtrPtr) == fsPtr) { + (*nextPtrPtr) = fsPtr->nextPtr; + removed = 1; + break; + } + } - fsPtr = (FileState *) chanPtr->instanceData; + /* + * 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. + */ - for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; - nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { - if ((*nextPtrPtr) == fsPtr) { - (*nextPtrPtr) = fsPtr->nextPtr; - removed = 1; - break; + if (!removed) { + panic("file info ptr not on thread channel list"); } } - - /* - * 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) - panic("file info ptr not on thread channel list"); - -#endif /* DEPRECATED */ } - -/* - *---------------------------------------------------------------------- - * - * TclpSpliceFileChannel -- - * - * Insert thread local ref for this channel. - * 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. */ -{ -#ifdef DEPRECATED - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - Channel *chanPtr = (Channel *) chan; - FileState *fsPtr; - - if (chanPtr->typePtr != &fileChannelType) - return; - - fsPtr = (FileState *) chanPtr->instanceData; - - fsPtr->nextPtr = tsdPtr->firstFilePtr; - tsdPtr->firstFilePtr = fsPtr; #endif /* DEPRECATED */ -} + diff --git a/unix/tclUnixPipe.c b/unix/tclUnixPipe.c index 77788ce..f410fc3 100644 --- a/unix/tclUnixPipe.c +++ b/unix/tclUnixPipe.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixPipe.c,v 1.23.2.2 2004/06/08 20:43:33 dgp Exp $ + * RCS: @(#) $Id: tclUnixPipe.c,v 1.23.2.3 2005/01/27 22:53:36 andreas_kupries Exp $ */ #include "tclInt.h" @@ -72,7 +72,7 @@ static int SetupStdFile _ANSI_ARGS_((TclFile file, int type)); static Tcl_ChannelType pipeChannelType = { "pipe", /* Type name. */ - TCL_CHANNEL_VERSION_2, /* v2 channel */ + TCL_CHANNEL_VERSION_4, /* v4 channel */ PipeCloseProc, /* Close proc. */ PipeInputProc, /* Input proc. */ PipeOutputProc, /* Output proc. */ @@ -85,6 +85,8 @@ static Tcl_ChannelType pipeChannelType = { PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */ + NULL, /* wide seek proc */ + NULL, /* thread action proc */ }; /* diff --git a/win/tclWinChan.c b/win/tclWinChan.c index 9333726..b743b55 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.30.2.2 2005/01/19 22:09:58 mdejong Exp $ + * RCS: @(#) $Id: tclWinChan.c,v 1.30.2.3 2005/01/27 22:53:36 andreas_kupries Exp $ */ #include "tclWinInt.h" @@ -98,6 +98,8 @@ static void FileSetupProc _ANSI_ARGS_((ClientData clientData, int flags)); static void FileWatchProc _ANSI_ARGS_((ClientData instanceData, int mask)); +static void FileThreadActionProc _ANSI_ARGS_ (( + ClientData instanceData, int action)); /* @@ -106,7 +108,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. */ @@ -120,6 +122,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) @@ -436,11 +439,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 Thread Action 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; } } @@ -1319,8 +1322,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; @@ -1389,10 +1395,9 @@ TclWinFlushDirtyChannels () /* *---------------------------------------------------------------------- * - * TclpCutFileChannel -- + * FileThreadActionProc -- * - * 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. @@ -1403,75 +1408,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; + FileInfo *infoPtr = (FileInfo *) instanceData; - if (chanPtr->typePtr != &fileChannelType) - return; + 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; + } + } - infoPtr = (FileInfo *) chanPtr->instanceData; + /* + * 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. + */ - for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; - nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { - if ((*nextPtrPtr) == infoPtr) { - (*nextPtrPtr) = infoPtr->nextPtr; - removed = 1; - break; + if (!removed) { + panic("file info ptr not on thread channel list"); } } - - /* - * 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) - panic("file info ptr not on thread channel list"); - -} - -/* - *---------------------------------------------------------------------- - * - * TclpSpliceFileChannel -- - * - * Insert thread local ref for this channel. - * 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; - - if (chanPtr->typePtr != &fileChannelType) - return; - - infoPtr = (FileInfo *) chanPtr->instanceData; - - infoPtr->nextPtr = tsdPtr->firstFilePtr; - tsdPtr->firstFilePtr = infoPtr; } diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index 6d3709c..2370a22 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.11 2002/11/26 22:41:58 davygrvy Exp $ + * RCS: @(#) $Id: tclWinConsole.c,v 1.11.2.1 2005/01/27 22:53:37 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; } /* @@ -1170,7 +1174,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); } @@ -1256,7 +1263,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); } @@ -1291,10 +1301,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. @@ -1305,9 +1314,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 @@ -1319,8 +1331,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 @@ -1361,3 +1371,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 aa54a29..57858f0 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.33.2.8 2004/05/10 20:55:44 davygrvy Exp $ + * RCS: @(#) $Id: tclWinPipe.c,v 1.33.2.9 2005/01/27 22:53:37 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 */ }; /* @@ -1689,6 +1694,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 @@ -2931,7 +2937,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); } @@ -3019,10 +3028,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 17678f2..0d363a5 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.25.2.1 2003/05/11 00:31:41 hobbs Exp $ + * RCS: @(#) $Id: tclWinSerial.c,v 1.25.2.2 2005/01/27 22:53:38 andreas_kupries Exp $ */ #include "tclWinInt.h" @@ -190,6 +190,9 @@ static int SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData, 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. @@ -197,7 +200,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. */ @@ -210,6 +213,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 */ }; /* @@ -1346,7 +1351,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); } @@ -1419,16 +1427,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 @@ -1440,14 +1457,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, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR @@ -2107,3 +2116,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 3d0d5f0..9af7d3e 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.2 2004/05/06 01:03:56 davygrvy Exp $ + * RCS: @(#) $Id: tclWinSock.c,v 1.36.2.3 2005/01/27 22:53:39 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 damanging 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. * *---------------------------------------------------------------------- */ @@ -1047,10 +1045,11 @@ NewSocketInfo(socket) infoPtr->acceptProc = 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; } @@ -1067,7 +1066,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. * *---------------------------------------------------------------------- */ @@ -2659,5 +2658,85 @@ TclWinGetServByName(const char * name, const char * proto) return winSock.getservbyname(name, proto); } + +/* + *---------------------------------------------------------------------- + * + * TcpThreadActionProc -- + * + * Insert or remove any thread local refs to this channel. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ +static void +TcpThreadActionProc (instanceData, action) + ClientData instanceData; + int action; +{ + ThreadSpecificData *tsdPtr; + SocketInfo *infoPtr = (SocketInfo *) instanceData; + int notifyCmd; + + if (action == TCL_CHANNEL_THREAD_INSERT) { + /* + * Ensure that socket subsystem is initialized in this thread, or + * else sockets will not work. + */ + + Tcl_MutexLock(&socketMutex); + InitSockets(); + Tcl_MutexUnlock(&socketMutex); + tsdPtr = TCL_TSD_INIT(&dataKey); + + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); + infoPtr->nextPtr = tsdPtr->socketList; + tsdPtr->socketList = infoPtr; + SetEvent(tsdPtr->socketListLock); + + 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); + + /* + * 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"); + } + + notifyCmd = UNSELECT; + } + + /* + * Ensure that, or stop, notifications for the socket occur in this thread. + */ + + SendMessage(tsdPtr->hwnd, SOCKET_SELECT, + (WPARAM) notifyCmd, (LPARAM) infoPtr); +} -- cgit v0.12