diff options
Diffstat (limited to 'generic/tclIORChan.c')
| -rw-r--r-- | generic/tclIORChan.c | 1714 | 
1 files changed, 1228 insertions, 486 deletions
| diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c index 28bc175..2fed3f4 100644 --- a/generic/tclIORChan.c +++ b/generic/tclIORChan.c @@ -14,12 +14,10 @@   *   * See the file "license.terms" for information on usage and redistribution of   * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclIORChan.c,v 1.14 2006/02/17 16:16:47 dgp Exp $   */ -#include <tclInt.h> -#include <tclIO.h> +#include "tclInt.h" +#include "tclIO.h"  #include <assert.h>  #ifndef EINVAL @@ -37,42 +35,51 @@ static int		ReflectClose(ClientData clientData,  			    Tcl_Interp *interp);  static int		ReflectInput(ClientData clientData, char *buf,  			    int toRead, int *errorCodePtr); -static int		ReflectOutput(ClientData clientData, CONST char *buf, +static int		ReflectOutput(ClientData clientData, const char *buf,  			    int toWrite, int *errorCodePtr);  static void		ReflectWatch(ClientData clientData, int mask);  static int		ReflectBlock(ClientData clientData, int mode); +#ifdef TCL_THREADS +static void		ReflectThread(ClientData clientData, int action); +#endif  static Tcl_WideInt	ReflectSeekWide(ClientData clientData,  			    Tcl_WideInt offset, int mode, int *errorCodePtr);  static int		ReflectSeek(ClientData clientData, long offset,  			    int mode, int *errorCodePtr);  static int		ReflectGetOption(ClientData clientData, -			    Tcl_Interp *interp, CONST char *optionName, +			    Tcl_Interp *interp, const char *optionName,  			    Tcl_DString *dsPtr);  static int		ReflectSetOption(ClientData clientData, -			    Tcl_Interp *interp, CONST char *optionName, -			    CONST char *newValue); +			    Tcl_Interp *interp, const char *optionName, +			    const char *newValue);  /*   * The C layer channel type/driver definition used by the reflection. This is   * a version 3 structure.   */ -static Tcl_ChannelType tclRChannelType = { -    "tclrchannel",	/* Type name.					*/ -    TCL_CHANNEL_VERSION_3, -    ReflectClose,	/* Close channel, clean instance data		*/ -    ReflectInput,	/* Handle read request				*/ -    ReflectOutput,	/* Handle write request				*/ -    ReflectSeek,	/* Move location of access point.    NULL'able	*/ -    ReflectSetOption,	/* Set options.			     NULL'able	*/ -    ReflectGetOption,	/* Get options.			     NULL'able	*/ -    ReflectWatch,	/* Initialize notifier				*/ -    NULL,		/* Get OS handle from the channel.   NULL'able	*/ -    NULL,		/* No close2 support.		     NULL'able	*/ -    ReflectBlock,	/* Set blocking/nonblocking.	     NULL'able	*/ -    NULL,		/* Flush channel. Not used by core.  NULL'able	*/ -    NULL,		/* Handle events.		     NULL'able	*/ -    ReflectSeekWide	/* Move access point (64 bit).	     NULL'able	*/ +static const Tcl_ChannelType tclRChannelType = { +    "tclrchannel",	   /* Type name.				  */ +    TCL_CHANNEL_VERSION_5, /* v5 channel */ +    ReflectClose,	   /* Close channel, clean instance data	  */ +    ReflectInput,	   /* Handle read request			  */ +    ReflectOutput,	   /* Handle write request			  */ +    ReflectSeek,	   /* Move location of access point.	NULL'able */ +    ReflectSetOption,	   /* Set options.			NULL'able */ +    ReflectGetOption,	   /* Get options.			NULL'able */ +    ReflectWatch,	   /* Initialize notifier			  */ +    NULL,		   /* Get OS handle from the channel.	NULL'able */ +    NULL,		   /* No close2 support.		NULL'able */ +    ReflectBlock,	   /* Set blocking/nonblocking.		NULL'able */ +    NULL,		   /* Flush channel. Not used by core.	NULL'able */ +    NULL,		   /* Handle events.			NULL'able */ +    ReflectSeekWide,	   /* Move access point (64 bit).	NULL'able */ +#ifdef TCL_THREADS +    ReflectThread,         /* thread action, tracking owner */ +#else +    NULL,		   /* thread action */ +#endif +    NULL		   /* truncate */  };  /* @@ -83,40 +90,26 @@ typedef struct {      Tcl_Channel chan;		/* Back reference to generic channel  				 * structure. */      Tcl_Interp *interp;		/* Reference to the interpreter containing the -				 * Tcl level part of the channel. */ +				 * Tcl level part of the channel. NULL here +				 * signals the channel is dead because the +				 * interpreter/thread containing its Tcl +				 * command is gone. +				 */  #ifdef TCL_THREADS -    Tcl_ThreadId thread;	/* Thread the 'interp' belongs to. */ +    Tcl_ThreadId thread;	/* Thread the 'interp' belongs to. == Handler thread */ +    Tcl_ThreadId owner;         /* Thread owning the structure.    == Channel thread */  #endif - -    /* See [==] as well. -     * Storage for the command prefix and the additional words required for -     * the invocation of methods in the command handler. -     * -     * argv [0] ... [.] | [argc-2] [argc-1] | [argc]  [argc+2] -     *      cmd ... pfx | method   chan     | detail1 detail2 -     *      ~~~~ CT ~~~            ~~ CT ~~ -     * -     * CT = Belongs to the 'Command handler Thread'. -     */ - -    int argc;			/* Number of preallocated words - 2 */ -    Tcl_Obj **argv;		/* Preallocated array for calling the handler. -				 * args[0] is placeholder for cmd word. -				 * Followed by the arguments in the prefix, -				 * plus 4 placeholders for method, channel, -				 * and at most two varying (method specific) -				 * words. */ -    int methods;		/* Bitmask of supported methods */ - -    /* -     * NOTE (9): Should we have predefined shared literals for the method -     * names? -     */ +    Tcl_Obj *cmd;		/* Callback command prefix */ +    Tcl_Obj *methods;		/* Methods to append to command prefix */ +    Tcl_Obj *name;		/* Name of the channel as created */      int mode;			/* Mask of R/W mode */      int interest;		/* Mask of events the channel is interested  				 * in. */ +    int dead;			/* Boolean signal that some operations +				 * should no longer be attempted. */ +      /*       * Note regarding the usage of timers.       * @@ -135,10 +128,27 @@ typedef struct {  } ReflectedChannel;  /* + * Structure of the table maping from channel handles to reflected + * channels. Each interpreter which has the handler command for one or more + * reflected channels records them in such a table, so that 'chan postevent' + * is able to find them even if the actual channel was moved to a different + * interpreter and/or thread. + * + * The table is reachable via the standard interpreter AssocData, the key is + * defined below. + */ + +typedef struct { +    Tcl_HashTable map; +} ReflectedChannelMap; + +#define RCMKEY "ReflectedChannelMap" + +/*   * Event literals. ==================================================   */ -static CONST char *eventOptions[] = { +static const char *const eventOptions[] = {      "read", "write", NULL  };  typedef enum { @@ -149,7 +159,7 @@ typedef enum {   * Method literals. ==================================================   */ -static CONST char *methodNames[] = { +static const char *const methodNames[] = {      "blocking",		/* OPT */      "cget",		/* OPT \/ Together or none */      "cgetall",		/* OPT /\ of these two     */ @@ -172,7 +182,7 @@ typedef enum {      METH_READ,      METH_SEEK,      METH_WATCH, -    METH_WRITE, +    METH_WRITE  } MethodName;  #define FLAG(m) (1 << (m)) @@ -215,9 +225,9 @@ typedef enum {  /*   * Event used to forward driver invocations to the thread actually managing - * the channel. We cannot construct the command to execute and forward - * that. Because then it will contain a mixture of Tcl_Obj's belonging to both - * the command handler thread (CT), and the thread managing the channel (MT), + * the channel. We cannot construct the command to execute and forward that. + * Because then it will contain a mixture of Tcl_Obj's belonging to both the + * command handler thread (CT), and the thread managing the channel (MT),   * executed in CT. Tcl_Obj's are not allowed to cross thread boundaries. So we   * forward an operation code, the argument details, and reference to results.   * The command is assembled in the CT and belongs fully to that thread. No @@ -245,7 +255,7 @@ struct ForwardParamInput {  };  struct ForwardParamOutput {      ForwardParamBase base;	/* "Supertype". MUST COME FIRST. */ -    CONST char *buf;		/* I: Where the bytes to write come from */ +    const char *buf;		/* I: Where the bytes to write come from */      int toWrite;		/* I: #bytes to write,  				 * O: #bytes actually written */  }; @@ -265,12 +275,12 @@ struct ForwardParamBlock {  };  struct ForwardParamSetOpt {      ForwardParamBase base;	/* "Supertype". MUST COME FIRST. */ -    CONST char *name;		/* Name of option to set */ -    CONST char *value;		/* Value to set */ +    const char *name;		/* Name of option to set */ +    const char *value;		/* Value to set */  };  struct ForwardParamGetOpt {      ForwardParamBase base;	/* "Supertype". MUST COME FIRST. */ -    CONST char *name;		/* Name of option to get, maybe NULL */ +    const char *name;		/* Name of option to get, maybe NULL */      Tcl_DString *value;		/* Result */  }; @@ -319,6 +329,14 @@ typedef struct ForwardingEvent {  struct ForwardingResult {      Tcl_ThreadId src;		/* Originating thread. */      Tcl_ThreadId dst;		/* Thread the op was forwarded to. */ +    Tcl_Interp *dsti;		/* Interpreter in the thread the op was +				 * forwarded to. */ +    /* +     * Note regarding 'dsti' above: Its information is also available via the +     * chain evPtr->rcPtr->interp, however, as can be seen, two more +     * indirections are needed to retrieve it. And the evPtr may be gone, +     * breaking the chain. +     */      Tcl_Condition done;		/* Condition variable the forwarder blocks  				 * on. */      int result;			/* TCL_OK or TCL_ERROR */ @@ -328,6 +346,17 @@ struct ForwardingResult {  				 * results. */  }; +typedef struct ThreadSpecificData { +    /* +     * Table of all reflected channels owned by this thread. This is the +     * per-thread version of the per-interpreter map. +     */ + +    ReflectedChannelMap *rcmPtr; +} ThreadSpecificData; + +static Tcl_ThreadDataKey dataKey; +  /*   * List of forwarded operations which have not completed yet, plus the mutex   * to protect the access to this process global list. @@ -342,41 +371,43 @@ TCL_DECLARE_MUTEX(rcForwardMutex)   * the event function executed by the thread receiving a forwarding event   * (which executes the appropriate function and collects the result, if any).   * - * The two ExitProcs are handlers so that things do not deadlock when either - * thread involved in the forwarding exits. They also clean things up so that - * we don't leak resources when threads go away. + * The ExitProc ensures that things do not deadlock when the sending thread + * involved in the forwarding exits. It also clean things up so that we don't + * leak resources when threads go away.   */ -static void		ForwardOpToOwnerThread(ReflectedChannel *rcPtr, -			    ForwardedOperation op, CONST VOID *param); +static void		ForwardOpToHandlerThread(ReflectedChannel *rcPtr, +			    ForwardedOperation op, const void *param);  static int		ForwardProc(Tcl_Event *evPtr, int mask);  static void		SrcExitProc(ClientData clientData); -static void		DstExitProc(ClientData clientData);  #define FreeReceivedError(p) \ -	if ((p)->base.mustFree) { \ -	    ckfree((p)->base.msgStr); \ +	if ((p)->base.mustFree) {                               \ +	    ckfree((p)->base.msgStr);                           \  	}  #define PassReceivedErrorInterp(i,p) \ -	if ((i) != NULL) { \ -	    Tcl_SetChannelErrorInterp((i), \ -		    Tcl_NewStringObj((p)->base.msgStr, -1)); \ -	} \ +	if ((i) != NULL) {                                      \ +	    Tcl_SetChannelErrorInterp((i),                      \ +		    Tcl_NewStringObj((p)->base.msgStr, -1));    \ +	}                                                       \  	FreeReceivedError(p)  #define PassReceivedError(c,p) \  	Tcl_SetChannelError((c), Tcl_NewStringObj((p)->base.msgStr, -1)); \  	FreeReceivedError(p)  #define ForwardSetStaticError(p,emsg) \ -	(p)->base.code = TCL_ERROR; \ -	(p)->base.mustFree = 0; \ +	(p)->base.code = TCL_ERROR;                             \ +	(p)->base.mustFree = 0;                                 \  	(p)->base.msgStr = (char *) (emsg)  #define ForwardSetDynamicError(p,emsg) \ -	(p)->base.code = TCL_ERROR; \ -	(p)->base.mustFree = 1; \ +	(p)->base.code = TCL_ERROR;                             \ +	(p)->base.mustFree = 1;                                 \  	(p)->base.msgStr = (char *) (emsg) -static void		ForwardSetObjError(ForwardParam *p, -			    Tcl_Obj *objPtr); +static void		ForwardSetObjError(ForwardParam *p, Tcl_Obj *objPtr); + +static ReflectedChannelMap *	GetThreadReflectedChannelMap(void); +static void		DeleteThreadReflectedChannelMap(ClientData clientData); +  #endif /* TCL_THREADS */  #define SetChannelErrorStr(c,msgStr) \ @@ -385,22 +416,28 @@ static void		ForwardSetObjError(ForwardParam *p,  static Tcl_Obj *	MarshallError(Tcl_Interp *interp);  static void		UnmarshallErrorResult(Tcl_Interp *interp,  			    Tcl_Obj *msgObj); - +  /*   * Static functions for this file:   */  static int		EncodeEventMask(Tcl_Interp *interp, -			    CONST char *objName, Tcl_Obj *obj, int *mask); +			    const char *objName, Tcl_Obj *obj, int *mask);  static Tcl_Obj *	DecodeEventMask(int mask);  static ReflectedChannel * NewReflectedChannel(Tcl_Interp *interp,  			    Tcl_Obj *cmdpfxObj, int mode, Tcl_Obj *handleObj);  static Tcl_Obj *	NextHandle(void);  static void		FreeReflectedChannel(ReflectedChannel *rcPtr);  static int		InvokeTclMethod(ReflectedChannel *rcPtr, -			    CONST char *method, Tcl_Obj *argOneObj, +			    MethodName method, Tcl_Obj *argOneObj,  			    Tcl_Obj *argTwoObj, Tcl_Obj **resultObjPtr); +static ReflectedChannelMap *	GetReflectedChannelMap(Tcl_Interp *interp); +static void		DeleteReflectedChannelMap(ClientData clientData, +			    Tcl_Interp *interp); +static int		ErrnoReturn(ReflectedChannel *rcPtr, Tcl_Obj *resObj); +static void		MarkDead(ReflectedChannel *rcPtr); +  /*   * Global constant strings (messages). ==================   * These string are used directly as bypass errors, thus they have to be valid @@ -408,16 +445,16 @@ static int		InvokeTclMethod(ReflectedChannel *rcPtr,   * list-quoting to keep the words of the message together. See also [x].   */ -static CONST char *msg_read_unsup = "{read not supported by Tcl driver}"; -static CONST char *msg_read_toomuch = "{read delivered more than requested}"; -static CONST char *msg_write_unsup = "{write not supported by Tcl driver}"; -static CONST char *msg_write_toomuch = "{write wrote more than requested}"; -static CONST char *msg_seek_beforestart = "{Tried to seek before origin}"; +static const char *msg_read_toomuch = "{read delivered more than requested}"; +static const char *msg_write_toomuch = "{write wrote more than requested}"; +static const char *msg_write_nothing = "{write wrote nothing}"; +static const char *msg_seek_beforestart = "{Tried to seek before origin}";  #ifdef TCL_THREADS -static CONST char *msg_send_originlost = "{Origin thread lost}"; -static CONST char *msg_send_dstlost = "{Destination thread lost}"; +static const char *msg_send_originlost = "{Channel thread lost}";  #endif /* TCL_THREADS */ - +static const char *msg_send_dstlost    = "{Owner lost}"; +static const char *msg_dstlost    = "-code 1 -level 0 -errorcode NONE -errorinfo {} -errorline 1 {Owner lost}"; +  /*   * Main methods to plug into the 'chan' ensemble'. ==================   */ @@ -445,7 +482,7 @@ TclChanCreateObjCmd(      ClientData clientData,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  {      ReflectedChannel *rcPtr;	/* Instance data of the new channel */      Tcl_Obj *rcId;		/* Handle of the new channel */ @@ -463,6 +500,11 @@ TclChanCreateObjCmd(      int methods;		/* Bitmask for supported methods. */      Channel *chanPtr;		/* 'chan' resolved to internal struct. */      Tcl_Obj *err;		/* Error message */ +    ReflectedChannelMap *rcmPtr; +				/* Map of reflected channels with handlers in +				 * this interp. */ +    Tcl_HashEntry *hPtr;	/* Entry in the above map */ +    int isNew;			/* Placeholder. */      /*       * Syntax:   chan create MODE CMDPREFIX @@ -496,7 +538,7 @@ TclChanCreateObjCmd(      /*       * Second argument is command prefix, i.e. list of words, first word is -     * name of handler command, other words are fixed arguments. Run +     * name of handler command, other words are fixed arguments. Run the       * 'initialize' method to get the list of supported methods. Validate       * this.       */ @@ -517,10 +559,6 @@ TclChanCreateObjCmd(      rcId = NextHandle();      rcPtr = NewReflectedChannel(interp, cmdObj, mode, rcId); -    chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr, -	    mode); -    rcPtr->chan = chan; -    chanPtr = (Channel *) chan;      /*       * Invoke 'initialize' and validate that the handler is present and ok. @@ -533,8 +571,10 @@ TclChanCreateObjCmd(       */      modeObj = DecodeEventMask(mode); -    result = InvokeTclMethod(rcPtr, "initialize", modeObj, NULL, &resObj); +    /* assert modeObj.refCount == 1 */ +    result = InvokeTclMethod(rcPtr, METH_INIT, modeObj, NULL, &resObj);      Tcl_DecrRefCount(modeObj); +      if (result != TCL_OK) {  	UnmarshallErrorResult(interp, resObj);  	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ @@ -549,12 +589,10 @@ TclChanCreateObjCmd(       */      if (Tcl_ListObjGetElements(NULL, resObj, &listc, &listv) != TCL_OK) { - -	err = Tcl_NewStringObj("chan handler \"", -1); -	Tcl_AppendObjToObj(err, cmdObj); -	Tcl_AppendToObj(err, " initialize\" returned non-list: ", -1); -	Tcl_AppendObjToObj(err, resObj); -	Tcl_SetObjResult(interp, err); +        Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "chan handler \"%s initialize\" returned non-list: %s", +                TclGetString(cmdObj), TclGetString(resObj))); +	Tcl_DecrRefCount(resObj);  	goto error;      } @@ -562,55 +600,52 @@ TclChanCreateObjCmd(      while (listc > 0) {  	if (Tcl_GetIndexFromObj(interp, listv[listc-1], methodNames,  		"method", TCL_EXACT, &methIndex) != TCL_OK) { -	    err = Tcl_NewStringObj("chan handler \"", -1); +	    TclNewLiteralStringObj(err, "chan handler \"");  	    Tcl_AppendObjToObj(err, cmdObj);  	    Tcl_AppendToObj(err, " initialize\" returned ", -1);  	    Tcl_AppendObjToObj(err, Tcl_GetObjResult(interp));  	    Tcl_SetObjResult(interp, err); +	    Tcl_DecrRefCount(resObj);  	    goto error;  	}  	methods |= FLAG(methIndex);  	listc--;      } +    Tcl_DecrRefCount(resObj);      if ((REQUIRED_METHODS & methods) != REQUIRED_METHODS) { -	err = Tcl_NewStringObj("chan handler \"", -1); -	Tcl_AppendObjToObj(err, cmdObj); -	Tcl_AppendToObj(err, "\" does not support all required methods", -1); -	Tcl_SetObjResult(interp, err); +        Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "chan handler \"%s\" does not support all required methods", +                TclGetString(cmdObj)));  	goto error;      }      if ((mode & TCL_READABLE) && !HAS(methods, METH_READ)) { -	err = Tcl_NewStringObj("chan handler \"", -1); -	Tcl_AppendObjToObj(err, cmdObj); -	Tcl_AppendToObj(err, "\" lacks a \"read\" method", -1); -	Tcl_SetObjResult(interp, err); +        Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "chan handler \"%s\" lacks a \"read\" method", +                TclGetString(cmdObj)));  	goto error;      }      if ((mode & TCL_WRITABLE) && !HAS(methods, METH_WRITE)) { -	err = Tcl_NewStringObj("chan handler \"", -1); -	Tcl_AppendObjToObj(err, cmdObj); -	Tcl_AppendToObj(err, "\" lacks a \"write\" method", -1); -	Tcl_SetObjResult(interp, err); +        Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "chan handler \"%s\" lacks a \"write\" method", +                TclGetString(cmdObj)));  	goto error;      }      if (!IMPLIES(HAS(methods, METH_CGET), HAS(methods, METH_CGETALL))) { -	err = Tcl_NewStringObj("chan handler \"", -1); -	Tcl_AppendObjToObj(err, cmdObj); -	Tcl_AppendToObj(err, "\" supports \"cget\" but not \"cgetall\"", -1); -	Tcl_SetObjResult(interp, err); +        Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "chan handler \"%s\" supports \"cget\" but not \"cgetall\"", +                TclGetString(cmdObj)));  	goto error;      }      if (!IMPLIES(HAS(methods, METH_CGETALL), HAS(methods, METH_CGET))) { -	err = Tcl_NewStringObj("chan handler \"", -1); -	Tcl_AppendObjToObj(err, cmdObj); -	Tcl_AppendToObj(err, "\" supports \"cgetall\" but not \"cget\"", -1); -	Tcl_SetObjResult(interp, err); +        Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "chan handler \"%s\" supports \"cgetall\" but not \"cget\"", +                TclGetString(cmdObj)));  	goto error;      } @@ -620,7 +655,11 @@ TclChanCreateObjCmd(       * Everything is fine now.       */ -    rcPtr->methods = methods; +    chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr, +	    mode); +    rcPtr->chan = chan; +    TclChannelPreserve(chan); +    chanPtr = (Channel *) chan;      if ((methods & NULLABLE_METHODS) != NULLABLE_METHODS) {  	/* @@ -629,8 +668,7 @@ TclChanCreateObjCmd(  	 * as the actual channel type.  	 */ -	Tcl_ChannelType *clonePtr = (Tcl_ChannelType *) -		ckalloc(sizeof(Tcl_ChannelType)); +	Tcl_ChannelType *clonePtr = ckalloc(sizeof(Tcl_ChannelType));  	memcpy(clonePtr, &tclRChannelType, sizeof(Tcl_ChannelType)); @@ -652,21 +690,40 @@ TclChanCreateObjCmd(  	chanPtr->typePtr = clonePtr;      } +    /* +     * Register the channel in the I/O system, and in our our map for 'chan +     * postevent'. +     */ +      Tcl_RegisterChannel(interp, chan); +    rcmPtr = GetReflectedChannelMap(interp); +    hPtr = Tcl_CreateHashEntry(&rcmPtr->map, chanPtr->state->channelName, +	    &isNew); +    if (!isNew && chanPtr != Tcl_GetHashValue(hPtr)) { +	Tcl_Panic("TclChanCreateObjCmd: duplicate channel names"); +    } +    Tcl_SetHashValue(hPtr, chan); +#ifdef TCL_THREADS +    rcmPtr = GetThreadReflectedChannelMap(); +    hPtr = Tcl_CreateHashEntry(&rcmPtr->map, chanPtr->state->channelName, +	    &isNew); +    Tcl_SetHashValue(hPtr, chan); +#endif +      /*       * Return handle as result of command.       */ -    Tcl_SetObjResult(interp, rcId); +    Tcl_SetObjResult(interp, +            Tcl_NewStringObj(chanPtr->state->channelName, -1));      return TCL_OK; - error: -    /* -     * Signal to ReflectClose to not call 'finalize'. -     */ -    rcPtr->methods = 0; -    Tcl_Close(interp, chan); +  error: +    Tcl_DecrRefCount(rcPtr->name); +    Tcl_DecrRefCount(rcPtr->methods); +    Tcl_DecrRefCount(rcPtr->cmd); +    ckfree((char*) rcPtr);      return TCL_ERROR;  #undef MODE @@ -691,14 +748,60 @@ TclChanCreateObjCmd(   *----------------------------------------------------------------------   */ +typedef struct ReflectEvent { +    Tcl_Event header; +    ReflectedChannel *rcPtr; +    int events; +} ReflectEvent; + +static int +ReflectEventRun( +    Tcl_Event *ev, +    int flags) +{ +    /* OWNER thread +     * +     * Note: When the channel is closed any pending events of this type are +     * deleted. See ReflectClose() for the Tcl_DeleteEvents() calls +     * accomplishing that. +     */ + +    ReflectEvent *e = (ReflectEvent *) ev; + +    Tcl_NotifyChannel(e->rcPtr->chan, e->events); +    return 1; +} + +static int +ReflectEventDelete( +    Tcl_Event *ev, +    ClientData cd) +{ +    /* OWNER thread +     * +     * Invoked by DeleteThreadReflectedChannelMap() and ReflectClose(). The +     * latter ensures that no pending events of this type are run on an +     * invalid channel. +     */ + +    ReflectEvent *e = (ReflectEvent *) ev; + +    if ((ev->proc != ReflectEventRun) || ((cd != NULL) && (cd != e->rcPtr))) { +        return 0; +    } +    return 1; +} +  int  TclChanPostEventObjCmd(      ClientData clientData,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  {      /* +     * Ensure -> HANDLER thread +     *       * Syntax:   chan postevent CHANNEL EVENTSPEC       *           [0]  [1]       [2]     [3]       * @@ -711,13 +814,15 @@ TclChanPostEventObjCmd(  #define CHAN	(1)  #define EVENT	(2) -    CONST char *chanId;		/* Tcl level channel handle */ +    const char *chanId;		/* Tcl level channel handle */      Tcl_Channel chan;		/* Channel associated to the handle */ -    Tcl_ChannelType *chanTypePtr; +    const Tcl_ChannelType *chanTypePtr;  				/* Its associated driver structure */      ReflectedChannel *rcPtr;	/* Associated instance data */ -    int mode;			/* Dummy, r|w mode of the channel */      int events;			/* Mask of events to post */ +    ReflectedChannelMap *rcmPtr;/* Map of reflected channels with handlers in +				 * this interp. */ +    Tcl_HashEntry *hPtr;	/* Entry in the above map */      /*       * Number of arguments... @@ -734,12 +839,34 @@ TclChanPostEventObjCmd(       */      chanId = TclGetString(objv[CHAN]); -    chan = Tcl_GetChannel(interp, chanId, &mode); -    if (chan == NULL) { +    rcmPtr = GetReflectedChannelMap(interp); +    hPtr = Tcl_FindHashEntry(&rcmPtr->map, chanId); + +    if (hPtr == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "can not find reflected channel named \"%s\"", chanId)); +	Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanId, NULL);  	return TCL_ERROR;      } +    /* +     * Note that the search above subsumes several of the older checks, namely: +     * +     * (1) Does the channel handle refer to a reflected channel ? +     * (2) Is the post event issued from the interpreter holding the handler +     *     of the reflected channel ? +     * +     * A successful search answers yes to both. Because the map holds only +     * handles of reflected channels, and only of such whose handler is +     * defined in this interpreter. +     * +     * We keep the old checks for both, for paranioa, but abort now instead of +     * throwing errors, as failure now means that our internal datastructures +     * have gone seriously haywire. +     */ + +    chan = Tcl_GetHashValue(hPtr);      chanTypePtr = Tcl_GetChannelType(chan);      /* @@ -752,17 +879,13 @@ TclChanPostEventObjCmd(       */      if (chanTypePtr->watchProc != &ReflectWatch) { -	Tcl_AppendResult(interp, "channel \"", chanId, -		"\" is not a reflected channel", NULL); -	return TCL_ERROR; +	Tcl_Panic("TclChanPostEventObjCmd: channel is not a reflected channel");      } -    rcPtr = (ReflectedChannel *) Tcl_GetChannelInstanceData(chan); +    rcPtr = Tcl_GetChannelInstanceData(chan);      if (rcPtr->interp != interp) { -	Tcl_AppendResult(interp, "postevent for channel \"", chanId, -		"\" called from outside interpreter", NULL); -	return TCL_ERROR; +	Tcl_Panic("TclChanPostEventObjCmd: postevent accepted for call from outside interpreter");      }      /* @@ -779,8 +902,9 @@ TclChanPostEventObjCmd(       */      if (events & ~rcPtr->interest) { -	Tcl_AppendResult(interp, "tried to post events channel \"", chanId, -		"\" is not interested in", NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "tried to post events channel \"%s\" is not interested in", +                chanId));  	return TCL_ERROR;      } @@ -788,7 +912,44 @@ TclChanPostEventObjCmd(       * We have the channel and the events to post.       */ -    Tcl_NotifyChannel(chan, events); +#ifdef TCL_THREADS +    if (rcPtr->owner == rcPtr->thread) { +#endif +        Tcl_NotifyChannel(chan, events); +#ifdef TCL_THREADS +    } else { +        ReflectEvent *ev = ckalloc(sizeof(ReflectEvent)); + +        ev->header.proc = ReflectEventRun; +        ev->events = events; +        ev->rcPtr = rcPtr; + +        /* +         * We are not preserving the structure here. When the channel is +         * closed any pending events are deleted, see ReflectClose(), and +         * ReflectEventDelete(). Trying to preserve and later release when the +         * event is run may generate a situation where the channel structure +         * is deleted but not our structure, crashing in +         * FreeReflectedChannel(). +         * +         * Force creation of the RCM, for proper cleanup on thread teardown. +         * The teardown of unprocessed events is currently coupled to the +         * thread reflected channel map +         */ + +        (void) GetThreadReflectedChannelMap(); + +        /* XXX Race condition !! +         * XXX The destination thread may not exist anymore already. +         * XXX (Delayed postevent executed after channel got removed). +         * XXX Can we detect this ? (check the validity of the owner threadid ?) +         * XXX Actually, in that case the channel should be dead also ! +         */ + +        Tcl_ThreadQueueEvent(rcPtr->owner, (Tcl_Event *) ev, TCL_QUEUE_TAIL); +        Tcl_ThreadAlert(rcPtr->owner); +    } +#endif      /*       * Squash interp results left by the event script. @@ -805,7 +966,7 @@ TclChanPostEventObjCmd(   * Channel error message marshalling utilities.   */ -static Tcl_Obj* +static Tcl_Obj *  MarshallError(      Tcl_Interp *interp)  { @@ -860,7 +1021,7 @@ UnmarshallErrorResult(      }      (void) Tcl_SetReturnOptions(interp, Tcl_NewListObj(numOptions, lv)); -    ((Interp *)interp)->flags &= ~ERR_ALREADY_LOGGED; +    ((Interp *) interp)->flags &= ~ERR_ALREADY_LOGGED;  }  int @@ -945,11 +1106,15 @@ ReflectClose(      ClientData clientData,      Tcl_Interp *interp)  { -    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; +    ReflectedChannel *rcPtr = clientData;      int result;			/* Result code for 'close' */      Tcl_Obj *resObj;		/* Result data for 'close' */ +    ReflectedChannelMap *rcmPtr;/* Map of reflected channels with handlers in +				 * this interp */ +    Tcl_HashEntry *hPtr;	/* Entry in the above map */ +    const Tcl_ChannelType *tctPtr; -    if (interp == NULL) { +    if (TclInThreadExit()) {  	/*  	 * This call comes from TclFinalizeIOSystem. There are no  	 * interpreters, and therefore we cannot call upon the handler command @@ -961,42 +1126,36 @@ ReflectClose(  	/*  	 * THREADED => Forward this to the origin thread  	 * -	 * Note: Have a thread delete handler for the origin thread. Use this -	 * to clean up the structure! +	 * Note: DeleteThreadReflectedChannelMap() is the thread exit handler +	 * for the origin thread. Use this to clean up the structure? Except +	 * if lost?  	 */  #ifdef TCL_THREADS  	if (rcPtr->thread != Tcl_GetCurrentThread()) {  	    ForwardParam p; -	    ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p); +	    ForwardOpToHandlerThread(rcPtr, ForwardedClose, &p);  	    result = p.base.code; -	    /* -	     * FreeReflectedChannel is done in the forwarded operation!, in -	     * the other thread. rcPtr here is gone! -	     */ +            /* +             * Now squash the pending reflection events for this channel. +             */ + +            Tcl_DeleteEvents(ReflectEventDelete, rcPtr);  	    if (result != TCL_OK) {  		FreeReceivedError(&p);  	    } -	    return EOK;  	}  #endif -	FreeReflectedChannel(rcPtr); -	return EOK; -    } - -    /* -     * -- No -- ASSERT rcPtr->methods & FLAG(METH_FINAL) -     * -     * A cleaned method mask here implies that the channel creation was -     * aborted, and "finalize" must not be called. -     */ - -    if (rcPtr->methods == 0) { -	FreeReflectedChannel(rcPtr); +	tctPtr = ((Channel *)rcPtr->chan)->typePtr; +	if (tctPtr && tctPtr != &tclRChannelType) { +	    ckfree(tctPtr); +	    ((Channel *)rcPtr->chan)->typePtr = NULL; +	} +        Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);  	return EOK;      } @@ -1008,30 +1167,64 @@ ReflectClose(      if (rcPtr->thread != Tcl_GetCurrentThread()) {  	ForwardParam p; -	ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p); +	ForwardOpToHandlerThread(rcPtr, ForwardedClose, &p);  	result = p.base.code; -	/* -	 * FreeReflectedChannel is done in the forwarded operation!, in the -	 * other thread. rcPtr here is gone! -	 */ +        /* +         * Now squash the pending reflection events for this channel. +         */ + +        Tcl_DeleteEvents(ReflectEventDelete, rcPtr);  	if (result != TCL_OK) {  	    PassReceivedErrorInterp(interp, &p);  	}      } else {  #endif -	result = InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj); +	result = InvokeTclMethod(rcPtr, METH_FINAL, NULL, NULL, &resObj);  	if ((result != TCL_OK) && (interp != NULL)) {  	    Tcl_SetChannelErrorInterp(interp, resObj);  	}  	Tcl_DecrRefCount(resObj);	/* Remove reference we held from the  					 * invoke */ + +	/* +	 * Remove the channel from the map before releasing the memory, to +	 * prevent future accesses (like by 'postevent') from finding and +	 * dereferencing a dangling pointer. +	 * +	 * NOTE: The channel may not be in the map. This is ok, that happens +	 * when the channel was created in a different interpreter and/or +	 * thread and then was moved here. +	 * +	 * NOTE: The channel may have been removed from the map already via +	 * the per-interp DeleteReflectedChannelMap exit-handler. +	 */ + +	if (!rcPtr->dead) { +	    rcmPtr = GetReflectedChannelMap(rcPtr->interp); +	    hPtr = Tcl_FindHashEntry(&rcmPtr->map, +		    Tcl_GetChannelName(rcPtr->chan)); +	    if (hPtr) { +		Tcl_DeleteHashEntry(hPtr); +	    } +	}  #ifdef TCL_THREADS -	FreeReflectedChannel(rcPtr); +	rcmPtr = GetThreadReflectedChannelMap(); +	hPtr = Tcl_FindHashEntry(&rcmPtr->map, +		Tcl_GetChannelName(rcPtr->chan)); +	if (hPtr) { +	    Tcl_DeleteHashEntry(hPtr); +	}      }  #endif +    tctPtr = ((Channel *)rcPtr->chan)->typePtr; +    if (tctPtr && tctPtr != &tclRChannelType) { +	    ckfree(tctPtr); +	    ((Channel *)rcPtr->chan)->typePtr = NULL; +    } +    Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);      return (result == TCL_OK) ? EOK : EINVAL;  } @@ -1058,25 +1251,13 @@ ReflectInput(      int toRead,      int *errorCodePtr)  { -    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; +    ReflectedChannel *rcPtr = clientData;      Tcl_Obj *toReadObj;      int bytec;			/* Number of returned bytes */      unsigned char *bytev;	/* Array of returned bytes */      Tcl_Obj *resObj;		/* Result data for 'read' */      /* -     * The following check can be done before thread redirection, because we -     * are reading from an item which is readonly, i.e. will never change -     * during the lifetime of the channel. -     */ - -    if (!(rcPtr->methods & FLAG(METH_READ))) { -	SetChannelErrorStr(rcPtr->chan, msg_read_unsup); -	*errorCodePtr = EINVAL; -	return -1; -    } - -    /*       * Are we in the correct thread?       */ @@ -1087,11 +1268,17 @@ ReflectInput(  	p.input.buf = buf;  	p.input.toRead = toRead; -	ForwardOpToOwnerThread(rcPtr, ForwardedInput, &p); +	ForwardOpToHandlerThread(rcPtr, ForwardedInput, &p);  	if (p.base.code != TCL_OK) { -	    PassReceivedError(rcPtr->chan, &p); -	    *errorCodePtr = EINVAL; +	    if (p.base.code < 0) { +		/* No error message, this is an errno signal. */ +		*errorCodePtr = -p.base.code; +	    } else { +		PassReceivedError(rcPtr->chan, &p); +		*errorCodePtr = EINVAL; +	    } +	    p.input.toRead = -1;  	} else {  	    *errorCodePtr = EOK;  	} @@ -1103,31 +1290,46 @@ ReflectInput(      /* ASSERT: rcPtr->method & FLAG(METH_READ) */      /* ASSERT: rcPtr->mode & TCL_READABLE */ +    Tcl_Preserve(rcPtr); +      toReadObj = Tcl_NewIntObj(toRead); -    if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK) { +    Tcl_IncrRefCount(toReadObj); + +    if (InvokeTclMethod(rcPtr, METH_READ, toReadObj, NULL, &resObj)!=TCL_OK) { +	int code = ErrnoReturn(rcPtr, resObj); + +	if (code < 0) { +	    *errorCodePtr = -code; +            goto error; +	} +  	Tcl_SetChannelError(rcPtr->chan, resObj); -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      }      bytev = Tcl_GetByteArrayFromObj(resObj, &bytec);      if (toRead < bytec) { -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */  	SetChannelErrorStr(rcPtr->chan, msg_read_toomuch); -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      }      *errorCodePtr = EOK;      if (bytec > 0) { -	memcpy(buf, bytev, (size_t)bytec); +	memcpy(buf, bytev, (size_t) bytec);      } + stop: +    Tcl_DecrRefCount(toReadObj);      Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */ +    Tcl_Release(rcPtr);      return bytec; + invalid: +    *errorCodePtr = EINVAL; + error: +    bytec = -1; +    goto stop;  }  /* @@ -1149,28 +1351,16 @@ ReflectInput(  static int  ReflectOutput(      ClientData clientData, -    CONST char *buf, +    const char *buf,      int toWrite,      int *errorCodePtr)  { -    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; +    ReflectedChannel *rcPtr = clientData;      Tcl_Obj *bufObj;      Tcl_Obj *resObj;		/* Result data for 'write' */      int written;      /* -     * The following check can be done before thread redirection, because we -     * are reading from an item which is readonly, i.e. will never change -     * during the lifetime of the channel. -     */ - -    if (!(rcPtr->methods & FLAG(METH_WRITE))) { -	SetChannelErrorStr(rcPtr->chan, msg_write_unsup); -	*errorCodePtr = EINVAL; -	return -1; -    } - -    /*       * Are we in the correct thread?       */ @@ -1181,11 +1371,17 @@ ReflectOutput(  	p.output.buf = buf;  	p.output.toWrite = toWrite; -	ForwardOpToOwnerThread(rcPtr, ForwardedOutput, &p); +	ForwardOpToHandlerThread(rcPtr, ForwardedOutput, &p);  	if (p.base.code != TCL_OK) { -	    PassReceivedError(rcPtr->chan, &p); -	    *errorCodePtr = EINVAL; +	    if (p.base.code < 0) { +		/* No error message, this is an errno signal. */ +		*errorCodePtr = -p.base.code; +	    } else { +                PassReceivedError(rcPtr->chan, &p); +                *errorCodePtr = EINVAL; +            } +	    p.output.toWrite = -1;  	} else {  	    *errorCodePtr = EOK;  	} @@ -1197,24 +1393,47 @@ ReflectOutput(      /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */      /* ASSERT: rcPtr->mode & TCL_WRITABLE */ +    Tcl_Preserve(rcPtr); +    Tcl_Preserve(rcPtr->interp); +      bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite); -    if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) { +    Tcl_IncrRefCount(bufObj); + +    if (InvokeTclMethod(rcPtr, METH_WRITE, bufObj, NULL, &resObj) != TCL_OK) { +	int code = ErrnoReturn(rcPtr, resObj); + +	if (code < 0) { +	    *errorCodePtr = -code; +            goto error; +	} +  	Tcl_SetChannelError(rcPtr->chan, resObj); -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      } +    if (Tcl_InterpDeleted(rcPtr->interp)) { +	/* +	 * The interp was destroyed during InvokeTclMethod(). +	 */ + +	SetChannelErrorStr(rcPtr->chan, msg_send_dstlost); +        goto invalid; +    }      if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) { -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */  	Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp)); -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      } -    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */ +    if ((written == 0) && (toWrite > 0)) { +	/* +	 * The handler claims to have written nothing of what it was +	 * given. That is bad. +	 */ -    if ((written == 0) || (toWrite < written)) { +	SetChannelErrorStr(rcPtr->chan, msg_write_nothing); +        goto invalid; +    } +    if (toWrite < written) {  	/*  	 * The handler claims to have written more than it was given. That is  	 * bad. Note that the I/O core would crash if we were to return this @@ -1222,12 +1441,21 @@ ReflectOutput(  	 */  	SetChannelErrorStr(rcPtr->chan, msg_write_toomuch); -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      }      *errorCodePtr = EOK; + stop: +    Tcl_DecrRefCount(bufObj); +    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */ +    Tcl_Release(rcPtr->interp); +    Tcl_Release(rcPtr);      return written; + invalid: +    *errorCodePtr = EINVAL; + error: +    written = -1; +    goto stop;  }  /* @@ -1253,9 +1481,8 @@ ReflectSeekWide(      int seekMode,      int *errorCodePtr)  { -    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; -    Tcl_Obj *offObj; -    Tcl_Obj *baseObj; +    ReflectedChannel *rcPtr = clientData; +    Tcl_Obj *offObj, *baseObj;      Tcl_Obj *resObj;		/* Result for 'seek' */      Tcl_WideInt newLoc; @@ -1270,11 +1497,12 @@ ReflectSeekWide(  	p.seek.seekMode = seekMode;  	p.seek.offset = offset; -	ForwardOpToOwnerThread(rcPtr, ForwardedSeek, &p); +	ForwardOpToHandlerThread(rcPtr, ForwardedSeek, &p);  	if (p.base.code != TCL_OK) {  	    PassReceivedError(rcPtr->chan, &p);  	    *errorCodePtr = EINVAL; +	    p.seek.offset = -1;  	} else {  	    *errorCodePtr = EOK;  	} @@ -1285,33 +1513,41 @@ ReflectSeekWide(      /* ASSERT: rcPtr->method & FLAG(METH_SEEK) */ -    offObj = Tcl_NewWideIntObj(offset); -    baseObj = Tcl_NewStringObj((seekMode == SEEK_SET) ? "start" : -	    ((seekMode == SEEK_CUR) ? "current" : "end"), -1); -    if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK) { +    Tcl_Preserve(rcPtr); + +    offObj  = Tcl_NewWideIntObj(offset); +    baseObj = Tcl_NewStringObj( +            (seekMode == SEEK_SET) ? "start" : +            (seekMode == SEEK_CUR) ? "current" : "end", -1); +    Tcl_IncrRefCount(offObj); +    Tcl_IncrRefCount(baseObj); + +    if (InvokeTclMethod(rcPtr, METH_SEEK, offObj, baseObj, &resObj)!=TCL_OK) {  	Tcl_SetChannelError(rcPtr->chan, resObj); -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      }      if (Tcl_GetWideIntFromObj(rcPtr->interp, resObj, &newLoc) != TCL_OK) { -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */  	Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp)); -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      } -    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */ -      if (newLoc < Tcl_LongAsWide(0)) {  	SetChannelErrorStr(rcPtr->chan, msg_seek_beforestart); -	*errorCodePtr = EINVAL; -	return -1; +        goto invalid;      }      *errorCodePtr = EOK; + stop: +    Tcl_DecrRefCount(offObj); +    Tcl_DecrRefCount(baseObj); +    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */ +    Tcl_Release(rcPtr);      return newLoc; + invalid: +    *errorCodePtr = EINVAL; +    newLoc = -1; +    goto stop;  }  static int @@ -1354,11 +1590,9 @@ ReflectWatch(      ClientData clientData,      int mask)  { -    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; +    ReflectedChannel *rcPtr = clientData;      Tcl_Obj *maskObj; -    /* ASSERT rcPtr->methods & FLAG(METH_WATCH) */ -      /*       * We restrict the interest to what the channel can support. IOW there       * will never be write events for a channel which is not writable. @@ -1375,8 +1609,6 @@ ReflectWatch(  	return;      } -    rcPtr->interest = mask; -      /*       * Are we in the correct thread?       */ @@ -1386,7 +1618,7 @@ ReflectWatch(  	ForwardParam p;  	p.watch.mask = mask; -	ForwardOpToOwnerThread(rcPtr, ForwardedWatch, &p); +	ForwardOpToHandlerThread(rcPtr, ForwardedWatch, &p);  	/*  	 * Any failure from the forward is ignored. We have no place to put @@ -1397,9 +1629,15 @@ ReflectWatch(      }  #endif +    Tcl_Preserve(rcPtr); + +    rcPtr->interest = mask;      maskObj = DecodeEventMask(mask); -    (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL); +    /* assert maskObj.refCount == 1 */ +    (void) InvokeTclMethod(rcPtr, METH_WATCH, maskObj, NULL, NULL);      Tcl_DecrRefCount(maskObj); + +    Tcl_Release(rcPtr);  }  /* @@ -1424,7 +1662,7 @@ ReflectBlock(      ClientData clientData,      int nonblocking)  { -    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; +    ReflectedChannel *rcPtr = clientData;      Tcl_Obj *blockObj;      int errorNum;		/* EINVAL or EOK (success). */      Tcl_Obj *resObj;		/* Result data for 'blocking' */ @@ -1439,7 +1677,7 @@ ReflectBlock(  	p.block.nonblocking = nonblocking; -	ForwardOpToOwnerThread(rcPtr, ForwardedBlock, &p); +	ForwardOpToHandlerThread(rcPtr, ForwardedBlock, &p);  	if (p.base.code != TCL_OK) {  	    PassReceivedError(rcPtr->chan, &p); @@ -1451,18 +1689,62 @@ ReflectBlock(  #endif      blockObj = Tcl_NewBooleanObj(!nonblocking); +    Tcl_IncrRefCount(blockObj); + +    Tcl_Preserve(rcPtr); -    if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj) != TCL_OK) { +    if (InvokeTclMethod(rcPtr,METH_BLOCKING,blockObj,NULL,&resObj)!=TCL_OK) {  	Tcl_SetChannelError(rcPtr->chan, resObj);  	errorNum = EINVAL;      } else {  	errorNum = EOK;      } +    Tcl_DecrRefCount(blockObj);      Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */ + +    Tcl_Release(rcPtr);      return errorNum;  } +#ifdef TCL_THREADS +/* + *---------------------------------------------------------------------- + * + * ReflectThread -- + * + *	This function is invoked to tell the channel about thread movements. + * + * Results: + *	None. + * + * Side effects: + *	Allocates memory. Arbitrary, as it calls upon a script. + * + *---------------------------------------------------------------------- + */ + +static void +ReflectThread( +    ClientData clientData, +    int action) +{ +    ReflectedChannel *rcPtr = clientData; + +    switch (action) { +    case TCL_CHANNEL_THREAD_INSERT: +        rcPtr->owner = Tcl_GetCurrentThread(); +        break; +    case TCL_CHANNEL_THREAD_REMOVE: +        rcPtr->owner = NULL; +        break; +    default: +        Tcl_Panic("Unknown thread action code."); +        break; +    } +} + +#endif  /*   *----------------------------------------------------------------------   * @@ -1483,12 +1765,11 @@ static int  ReflectSetOption(      ClientData clientData,	/* Channel to query */      Tcl_Interp *interp,		/* Interpreter to leave error messages in */ -    CONST char *optionName,	/* Name of requested option */ -    CONST char *newValue)	/* The new value */ +    const char *optionName,	/* Name of requested option */ +    const char *newValue)	/* The new value */  { -    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; -    Tcl_Obj *optionObj; -    Tcl_Obj *valueObj; +    ReflectedChannel *rcPtr = clientData; +    Tcl_Obj *optionObj, *valueObj;      int result;			/* Result code for 'configure' */      Tcl_Obj *resObj;		/* Result data for 'configure' */ @@ -1503,7 +1784,7 @@ ReflectSetOption(  	p.setOpt.name = optionName;  	p.setOpt.value = newValue; -	ForwardOpToOwnerThread(rcPtr, ForwardedSetOpt, &p); +	ForwardOpToHandlerThread(rcPtr, ForwardedSetOpt, &p);  	if (p.base.code != TCL_OK) {  	    Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1); @@ -1516,15 +1797,23 @@ ReflectSetOption(  	return p.base.code;      }  #endif +    Tcl_Preserve(rcPtr);      optionObj = Tcl_NewStringObj(optionName, -1);      valueObj = Tcl_NewStringObj(newValue, -1); -    result = InvokeTclMethod(rcPtr, "configure",optionObj,valueObj, &resObj); + +    Tcl_IncrRefCount(optionObj); +    Tcl_IncrRefCount(valueObj); + +    result = InvokeTclMethod(rcPtr, METH_CONFIGURE,optionObj,valueObj, &resObj);      if (result != TCL_OK) {  	UnmarshallErrorResult(interp, resObj);      } +    Tcl_DecrRefCount(optionObj); +    Tcl_DecrRefCount(valueObj);      Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */ +    Tcl_Release(rcPtr);      return result;  } @@ -1548,7 +1837,7 @@ static int  ReflectGetOption(      ClientData clientData,	/* Channel to query */      Tcl_Interp *interp,		/* Interpreter to leave error messages in */ -    CONST char *optionName,	/* Name of reuqested option */ +    const char *optionName,	/* Name of reuqested option */      Tcl_DString *dsPtr)		/* String to place the result into */  {      /* @@ -1556,12 +1845,12 @@ ReflectGetOption(       * The bypass functions are not required.       */ -    ReflectedChannel *rcPtr = (ReflectedChannel*) clientData; +    ReflectedChannel *rcPtr = clientData;      Tcl_Obj *optionObj;      Tcl_Obj *resObj;		/* Result data for 'configure' */ -    int listc; +    int listc, result = TCL_OK;      Tcl_Obj **listv; -    const char *method; +    MethodName method;      /*       * Are we in the correct thread? @@ -1581,7 +1870,7 @@ ReflectGetOption(  	    opcode = ForwardedGetOpt;  	} -	ForwardOpToOwnerThread(rcPtr, opcode, &p); +	ForwardOpToHandlerThread(rcPtr, opcode, &p);  	if (p.base.code != TCL_OK) {  	    Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1); @@ -1600,21 +1889,23 @@ ReflectGetOption(  	 * Retrieve all options.  	 */ -	method = "cgetall"; +	method = METH_CGETALL;  	optionObj = NULL;      } else {  	/*  	 * Retrieve the value of one option.  	 */ -	method = "cget"; +	method = METH_CGET;  	optionObj = Tcl_NewStringObj(optionName, -1); +        Tcl_IncrRefCount(optionObj);      } +    Tcl_Preserve(rcPtr); +      if (InvokeTclMethod(rcPtr, method, optionObj, NULL, &resObj)!=TCL_OK) {  	UnmarshallErrorResult(interp, resObj); -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	return TCL_ERROR; +        goto error;      }      /* @@ -1623,9 +1914,8 @@ ReflectGetOption(       */      if (optionObj != NULL) { -	Tcl_DStringAppend(dsPtr, TclGetString(resObj), -1); -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	return TCL_OK; +	TclDStringAppendObj(dsPtr, resObj); +        goto ok;      }      /* @@ -1640,8 +1930,7 @@ ReflectGetOption(       */      if (Tcl_ListObjGetElements(interp, resObj, &listc, &listv) != TCL_OK) { -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	return TCL_ERROR; +        goto error;      }      if ((listc % 2) == 1) { @@ -1649,26 +1938,35 @@ ReflectGetOption(  	 * Odd number of elements is wrong.  	 */ -	Tcl_Obj *objPtr = Tcl_NewObj(); -  	Tcl_ResetResult(interp); -	TclObjPrintf(NULL, objPtr, "Expected list with even number of " +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"Expected list with even number of "  		"elements, got %d element%s instead", listc, -		(listc == 1 ? "" : "s")); -	Tcl_SetObjResult(interp, objPtr); -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	return TCL_ERROR; +		(listc == 1 ? "" : "s"))); +        goto error;      } else {  	int len; -	char *str = Tcl_GetStringFromObj(resObj, &len); +	const char *str = TclGetStringFromObj(resObj, &len);  	if (len) { -	    Tcl_DStringAppend(dsPtr, " ", 1); +	    TclDStringAppendLiteral(dsPtr, " ");  	    Tcl_DStringAppend(dsPtr, str, len);  	} -	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ -	return TCL_OK; +        goto ok;      } + + ok: +    result = TCL_OK; + stop: +    if (optionObj) { +        Tcl_DecrRefCount(optionObj); +    } +    Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */ +    Tcl_Release(rcPtr); +    return result; + error: +    result = TCL_ERROR; +    goto stop;  }  /* @@ -1681,9 +1979,9 @@ ReflectGetOption(   * EncodeEventMask --   *   *	This function takes a list of event items and constructs the - *	equivalent internal bitmask. The list has to contain at least one - *	element. Elements are "read", "write", or any unique abbreviation - *	thereof. Note that the bitmask is not changed if problems are + *	equivalent internal bitmask. The list must contain at least one + *	element. Elements are "read", "write", or any unique abbreviation of + *	them. Note that the bitmask is not changed if problems are   *	encountered.   *   * Results: @@ -1700,7 +1998,7 @@ ReflectGetOption(  static int  EncodeEventMask(      Tcl_Interp *interp, -    CONST char *objName, +    const char *objName,      Tcl_Obj *obj,      int *mask)  { @@ -1715,7 +2013,8 @@ EncodeEventMask(      }      if (listc < 1) { -	Tcl_AppendResult(interp, "bad ", objName, " list: is empty", NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "bad %s list: is empty", objName));  	return TCL_ERROR;      } @@ -1748,7 +2047,7 @@ EncodeEventMask(   *	This function takes an internal bitmask of events and constructs the   *	equivalent list of event items.   * - * Results: + * Results, Contract:   *	A Tcl_Obj reference. The object will have a refCount of one. The user   *	has to decrement it to release the object.   * @@ -1762,7 +2061,7 @@ static Tcl_Obj *  DecodeEventMask(      int mask)  { -    register CONST char *eventStr; +    register const char *eventStr;      Tcl_Obj *evObj;      switch (mask & RANDW) { @@ -1782,6 +2081,7 @@ DecodeEventMask(      evObj = Tcl_NewStringObj(eventStr, -1);      Tcl_IncrRefCount(evObj); +    /* assert evObj.refCount == 1 */      return evObj;  } @@ -1810,67 +2110,32 @@ NewReflectedChannel(      Tcl_Obj *handleObj)  {      ReflectedChannel *rcPtr; -    int listc; -    Tcl_Obj **listv; -    int i; +    MethodName mn = METH_BLOCKING; -    rcPtr = (ReflectedChannel *) ckalloc(sizeof(ReflectedChannel)); +    rcPtr = ckalloc(sizeof(ReflectedChannel));      /* rcPtr->chan: Assigned by caller. Dummy data here. */ -    /* rcPtr->methods: Assigned by caller. Dummy data here. */      rcPtr->chan = NULL; -    rcPtr->methods = 0;      rcPtr->interp = interp; +    rcPtr->dead = 0;  #ifdef TCL_THREADS      rcPtr->thread = Tcl_GetCurrentThread();  #endif      rcPtr->mode = mode;      rcPtr->interest = 0;		/* Initially no interest registered */ -    /* -     * Method placeholder. -     */ -      /* ASSERT: cmdpfxObj is a Tcl List */ - -    Tcl_ListObjGetElements(interp, cmdpfxObj, &listc, &listv); - -    /* -     * See [==] as well. -     * Storage for the command prefix and the additional words required for -     * the invocation of methods in the command handler. -     * -     * listv [0] [listc-1] | [listc]  [listc+1] | -     * argv  [0]   ... [.] | [argc-2] [argc-1]  | [argc]  [argc+2] -     *       cmd   ... pfx | method   chan      | detail1 detail2 -     */ - -    rcPtr->argc = listc + 2; -    rcPtr->argv = (Tcl_Obj**) ckalloc(sizeof(Tcl_Obj*) * (listc+4)); - -    /* -     * Duplicate object references. -     */ - -    for (i=0; i<listc ; i++) { -	Tcl_Obj *word = rcPtr->argv[i] = listv[i]; -	Tcl_IncrRefCount(word); -    } - -    i++;				/* Skip placeholder for method */ - -    rcPtr->argv[i] = handleObj; -    Tcl_IncrRefCount(handleObj); - -    /* -     * The next two objects are kept empty, varying arguments. -     */ - -    /* -     * Initialization complete. -     */ - +    rcPtr->cmd = TclListObjCopy(NULL, cmdpfxObj); +    Tcl_IncrRefCount(rcPtr->cmd); +    rcPtr->methods = Tcl_NewListObj(METH_WRITE + 1, NULL); +    while (mn <= METH_WRITE) { +	Tcl_ListObjAppendElement(NULL, rcPtr->methods, +		Tcl_NewStringObj(methodNames[mn++], -1)); +    } +    Tcl_IncrRefCount(rcPtr->methods); +    rcPtr->name = handleObj; +    Tcl_IncrRefCount(rcPtr->name);      return rcPtr;  } @@ -1908,9 +2173,8 @@ NextHandle(void)      static unsigned long rcCounter = 0;      Tcl_Obj *resObj; -    TclNewObj(resObj);      Tcl_MutexLock(&rcCounterMutex); -    TclObjPrintf(NULL, resObj, "rc%lu", rcCounter); +    resObj = Tcl_ObjPrintf("rc%lu", rcCounter);      rcCounter++;      Tcl_MutexUnlock(&rcCounterMutex); @@ -1918,27 +2182,22 @@ NextHandle(void)  }  static void -FreeReflectedChannel(rcPtr) -    ReflectedChannel *rcPtr; +FreeReflectedChannel( +    ReflectedChannel *rcPtr)  {      Channel *chanPtr = (Channel *) rcPtr->chan; -    int i, n; - -    if (chanPtr->typePtr != &tclRChannelType) { -	/* -	 * Delete a cloned ChannelType structure. -	 */ -	ckfree((char*) chanPtr->typePtr); +    TclChannelRelease((Tcl_Channel)chanPtr); +    if (rcPtr->name) { +	Tcl_DecrRefCount(rcPtr->name);      } - -    n = rcPtr->argc - 2; -    for (i=0; i<n; i++) { -	Tcl_DecrRefCount(rcPtr->argv[i]); +    if (rcPtr->methods) { +	Tcl_DecrRefCount(rcPtr->methods);      } - -    ckfree((char*) rcPtr->argv); -    ckfree((char*) rcPtr); +    if (rcPtr->cmd) { +	Tcl_DecrRefCount(rcPtr->cmd); +    } +    ckfree(rcPtr);  }  /* @@ -1957,52 +2216,71 @@ FreeReflectedChannel(rcPtr)   * Side effects:   *	Arbitrary, as it calls upon a Tcl script.   * + * Contract: + *	argOneObj.refCount >= 1 on entry and exit, if argOneObj != NULL + *	argTwoObj.refCount >= 1 on entry and exit, if argTwoObj != NULL + *	resObj.refCount in {0, 1, ...} + *   *----------------------------------------------------------------------   */  static int  InvokeTclMethod(      ReflectedChannel *rcPtr, -    CONST char *method, +    MethodName method,      Tcl_Obj *argOneObj,		/* NULL'able */      Tcl_Obj *argTwoObj,		/* NULL'able */      Tcl_Obj **resultObjPtr)	/* NULL'able */  { -    int cmdc;			/* #words in constructed command */      Tcl_Obj *methObj = NULL;	/* Method name in object form */      Tcl_InterpState sr;		/* State of handler interp */      int result;			/* Result code of method invokation */      Tcl_Obj *resObj = NULL;	/* Result of method invokation. */ +    Tcl_Obj *cmd; -    /* -     * NOTE (5): Decide impl. issue: Cache objects with method names? -     * Requires TSD data as reflections can be created in many different -     * threads. -     */ +    if (rcPtr->dead) { +	/* +	 * The channel is marked as dead. Bail out immediately, with an +	 * appropriate error. +	 */ + +	if (resultObjPtr != NULL) { +	    resObj = Tcl_NewStringObj(msg_dstlost,-1); +	    *resultObjPtr = resObj; +	    Tcl_IncrRefCount(resObj); +	} + +        /* +         * Not touching argOneObj, argTwoObj, they have not been used. +         * See the contract as well. +         */ + +	return TCL_ERROR; +    }      /* -     * Insert method into the pre-allocated area, after the command prefix, +     * Insert method into the callback command, after the command prefix,       * before the channel id.       */ -    methObj = Tcl_NewStringObj(method, -1); -    Tcl_IncrRefCount(methObj); -    rcPtr->argv[rcPtr->argc - 2] = methObj; +    cmd = TclListObjCopy(NULL, rcPtr->cmd); + +    Tcl_ListObjIndex(NULL, rcPtr->methods, method, &methObj); +    Tcl_ListObjAppendElement(NULL, cmd, methObj); +    Tcl_ListObjAppendElement(NULL, cmd, rcPtr->name);      /*       * Append the additional argument containing method specific details       * behind the channel id. If specified. +     * +     * Because of the contract there is no need to increment the refcounts. +     * The objects will survive the Tcl_EvalObjv without change.       */ -    cmdc = rcPtr->argc;      if (argOneObj) { -	Tcl_IncrRefCount(argOneObj); -	rcPtr->argv[cmdc] = argOneObj; -	cmdc++; +	Tcl_ListObjAppendElement(NULL, cmd, argOneObj);  	if (argTwoObj) { -	    Tcl_IncrRefCount(argTwoObj); -	    rcPtr->argv[cmdc] = argTwoObj; -	    cmdc++; +	    Tcl_ListObjAppendElement(NULL, cmd, argTwoObj);  	}      } @@ -2011,9 +2289,10 @@ InvokeTclMethod(       * existing state intact.       */ +    Tcl_IncrRefCount(cmd);      sr = Tcl_SaveInterpState(rcPtr->interp, 0 /* Dummy */);      Tcl_Preserve(rcPtr->interp); -    result = Tcl_EvalObjv(rcPtr->interp, cmdc, rcPtr->argv, TCL_EVAL_GLOBAL); +    result = Tcl_EvalObjEx(rcPtr->interp, cmd, TCL_EVAL_GLOBAL);      /*       * We do not try to extract the result information if the caller has no @@ -2037,43 +2316,32 @@ InvokeTclMethod(  	     * This is complex and ugly, and would be completely unnecessary  	     * if we only added support for a TCL_FORBID_EXCEPTIONS flag.  	     */ +  	    if (result != TCL_ERROR) { -		Tcl_Obj *cmd = Tcl_NewListObj(cmdc, rcPtr->argv);  		int cmdLen; -		CONST char *cmdString = Tcl_GetStringFromObj(cmd, &cmdLen); -		Tcl_Obj *msg = Tcl_NewObj(); +		const char *cmdString = TclGetStringFromObj(cmd, &cmdLen);  		Tcl_IncrRefCount(cmd); -		TclObjPrintf(NULL, msg, "chan handler returned bad code: %d", -			result);  		Tcl_ResetResult(rcPtr->interp); -		Tcl_SetObjResult(rcPtr->interp, msg); -		Tcl_LogCommandInfo(rcPtr->interp, cmdString, cmdString, cmdLen); +		Tcl_SetObjResult(rcPtr->interp, Tcl_ObjPrintf( +			"chan handler returned bad code: %d", result)); +		Tcl_LogCommandInfo(rcPtr->interp, cmdString, cmdString, +			cmdLen);  		Tcl_DecrRefCount(cmd);  		result = TCL_ERROR;  	    } -	    TclFormatToErrorInfo(rcPtr->interp, -		    "\n    (chan handler subcommand \"%s\")", method); +	    Tcl_AppendObjToErrorInfo(rcPtr->interp, Tcl_ObjPrintf( +		    "\n    (chan handler subcommand \"%s\")", +		    methodNames[method]));  	    resObj = MarshallError(rcPtr->interp);  	}  	Tcl_IncrRefCount(resObj);      } +    Tcl_DecrRefCount(cmd);      Tcl_RestoreInterpState(rcPtr->interp, sr);      Tcl_Release(rcPtr->interp);      /* -     * Cleanup of the dynamic parts of the command. -     */ - -    Tcl_DecrRefCount(methObj); -    if (argOneObj) { -	Tcl_DecrRefCount(argOneObj); -	if (argTwoObj) { -	    Tcl_DecrRefCount(argTwoObj); -	} -    } - -    /*       * The resObj has a ref count of 1 at this location. This means that the       * caller of InvokeTclMethod has to dispose of it (but only if it was       * returned to it). @@ -2091,24 +2359,440 @@ InvokeTclMethod(      return result;  } +/* + *---------------------------------------------------------------------- + * + * ErrnoReturn -- + * + *	Checks a method error result if it returned an 'errno'. + * + * Results: + *	The negative errno found in the error result, or 0. + * + * Side effects: + *	None. + * + * Users: + *	ReflectInput/Output(), to enable the signaling of EAGAIN + *	on 0-sized short reads/writes. + * + *---------------------------------------------------------------------- + */ + +static int +ErrnoReturn( +    ReflectedChannel *rcPtr, +    Tcl_Obj *resObj) +{ +    int code; +    Tcl_InterpState sr;		/* State of handler interp */ + +    if (rcPtr->dead) { +	return 0; +    } + +    sr = Tcl_SaveInterpState(rcPtr->interp, 0 /* Dummy */); +    UnmarshallErrorResult(rcPtr->interp, resObj); + +    resObj = Tcl_GetObjResult(rcPtr->interp); + +    if (((Tcl_GetIntFromObj(rcPtr->interp, resObj, &code) != TCL_OK) +	    || (code >= 0))) { +	if (strcmp("EAGAIN", TclGetString(resObj)) == 0) { +	    code = -EAGAIN; +	} else { +	    code = 0; +	} +    } + +    Tcl_RestoreInterpState(rcPtr->interp, sr); +    return code; +} + +/* + *---------------------------------------------------------------------- + * + * GetReflectedChannelMap -- + * + *	Gets and potentially initializes the reflected channel map for an + *	interpreter. + * + * Results: + *	A pointer to the map created, for use by the caller. + * + * Side effects: + *	Initializes the reflected channel map for an interpreter. + * + *---------------------------------------------------------------------- + */ + +static ReflectedChannelMap * +GetReflectedChannelMap( +    Tcl_Interp *interp) +{ +    ReflectedChannelMap *rcmPtr = Tcl_GetAssocData(interp, RCMKEY, NULL); + +    if (rcmPtr == NULL) { +	rcmPtr = ckalloc(sizeof(ReflectedChannelMap)); +	Tcl_InitHashTable(&rcmPtr->map, TCL_STRING_KEYS); +	Tcl_SetAssocData(interp, RCMKEY, +		(Tcl_InterpDeleteProc *) DeleteReflectedChannelMap, rcmPtr); +    } +    return rcmPtr; +} + +/* + *---------------------------------------------------------------------- + * + * DeleteReflectedChannelMap -- + * + *	Deletes the channel table for an interpreter, closing any open + *	channels whose refcount reaches zero. This procedure is invoked when + *	an interpreter is deleted, via the AssocData cleanup mechanism. + * + * Results: + *	None. + * + * Side effects: + *	Deletes the hash table of channels. May close channels. May flush + *	output on closed channels. Removes any channeEvent handlers that were + *	registered in this interpreter. + * + *---------------------------------------------------------------------- + */ + +static void +MarkDead( +    ReflectedChannel *rcPtr) +{ +    if (rcPtr->dead) { +	return; +    } +    if (rcPtr->name) { +	Tcl_DecrRefCount(rcPtr->name); +	rcPtr->name = NULL; +    } +    if (rcPtr->methods) { +	Tcl_DecrRefCount(rcPtr->methods); +	rcPtr->methods = NULL; +    } +    if (rcPtr->cmd) { +	Tcl_DecrRefCount(rcPtr->cmd); +	rcPtr->cmd = NULL; +    } +    rcPtr->dead = 1; +} + +static void +DeleteReflectedChannelMap( +    ClientData clientData,	/* The per-interpreter data structure. */ +    Tcl_Interp *interp)		/* The interpreter being deleted. */ +{ +    ReflectedChannelMap *rcmPtr = clientData; +				/* The map */ +    Tcl_HashSearch hSearch;	 /* Search variable. */ +    Tcl_HashEntry *hPtr;	 /* Search variable. */ +    ReflectedChannel *rcPtr; +    Tcl_Channel chan; +#ifdef TCL_THREADS +    ForwardingResult *resultPtr; +    ForwardingEvent *evPtr; +    ForwardParam *paramPtr; +#endif + +    /* +     * Delete all entries. The channels may have been closed already, or will +     * be closed later, by the standard IO finalization of an interpreter +     * under destruction. Except for the channels which were moved to a +     * different interpreter and/or thread. They do not exist from the IO +     * systems point of view and will not get closed. Therefore mark all as +     * dead so that any future access will cause a proper error. For channels +     * in a different thread we actually do the same as +     * DeleteThreadReflectedChannelMap(), just restricted to the channels of +     * this interp. +     */ + +    for (hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch); +	    hPtr != NULL; +	    hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch)) { +	chan = Tcl_GetHashValue(hPtr); +	rcPtr = Tcl_GetChannelInstanceData(chan); + +	MarkDead(rcPtr); +	Tcl_DeleteHashEntry(hPtr); +    } +    Tcl_DeleteHashTable(&rcmPtr->map); +    ckfree(&rcmPtr->map); +  #ifdef TCL_THREADS +    /* +     * The origin interpreter for one or more reflected channels is gone. +     */ + +    /* +     * Go through the list of pending results and cancel all whose events were +     * destined for this interpreter. While this is in progress we block any +     * other access to the list of pending results. +     */ + +    Tcl_MutexLock(&rcForwardMutex); + +    for (resultPtr = forwardList; +	    resultPtr != NULL; +	    resultPtr = resultPtr->nextPtr) { +	if (resultPtr->dsti != interp) { +	    /* +	     * Ignore results/events for other interpreters. +	     */ + +	    continue; +	} + +	/* +	 * The receiver for the event exited, before processing the event. We +	 * detach the result now, wake the originator up and signal failure. +         * +         * Attention: Results may have been detached already, by either the +         * receiver, or this thread, as part of other parts in the thread +         * teardown. Such results are ignored. See ticket [b47b176adf] for the +         * identical race condition in Tcl 8.6 IORTrans. +	 */ + +	evPtr = resultPtr->evPtr; + +	/* Basic crash safety until this routine can get revised [3411310] */ +	if (evPtr == NULL) { +	    continue; +	} +	paramPtr = evPtr->param; +	if (!evPtr) { +	    continue; +	} + +	evPtr->resultPtr = NULL; +	resultPtr->evPtr = NULL; +	resultPtr->result = TCL_ERROR; + +	ForwardSetStaticError(paramPtr, msg_send_dstlost); + +	Tcl_ConditionNotify(&resultPtr->done); +    } +    Tcl_MutexUnlock(&rcForwardMutex); + +    /* +     * Get the map of all channels handled by the current thread. This is a +     * ReflectedChannelMap, but on a per-thread basis, not per-interp. Go +     * through the channels and remove all which were handled by this +     * interpreter. They have already been marked as dead. +     */ + +    rcmPtr = GetThreadReflectedChannelMap(); +    for (hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch); +	    hPtr != NULL; +	    hPtr = Tcl_NextHashEntry(&hSearch)) { +	chan = Tcl_GetHashValue(hPtr); +	rcPtr = Tcl_GetChannelInstanceData(chan); + +	if (rcPtr->interp != interp) { +	    /* +	     * Ignore entries for other interpreters. +	     */ + +	    continue; +	} + +	MarkDead(rcPtr); +	Tcl_DeleteHashEntry(hPtr); +    } +#endif +} + +#ifdef TCL_THREADS +/* + *---------------------------------------------------------------------- + * + * GetThreadReflectedChannelMap -- + * + *	Gets and potentially initializes the reflected channel map for a + *	thread. + * + * Results: + *	A pointer to the map created, for use by the caller. + * + * Side effects: + *	Initializes the reflected channel map for a thread. + * + *---------------------------------------------------------------------- + */ + +static ReflectedChannelMap * +GetThreadReflectedChannelMap(void) +{ +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + +    if (!tsdPtr->rcmPtr) { +	tsdPtr->rcmPtr = ckalloc(sizeof(ReflectedChannelMap)); +	Tcl_InitHashTable(&tsdPtr->rcmPtr->map, TCL_STRING_KEYS); +	Tcl_CreateThreadExitHandler(DeleteThreadReflectedChannelMap, NULL); +    } + +    return tsdPtr->rcmPtr; +} + +/* + *---------------------------------------------------------------------- + * + * DeleteThreadReflectedChannelMap -- + * + *	Deletes the channel table for a thread. This procedure is invoked when + *	a thread is deleted. The channels have already been marked as dead, in + *	DeleteReflectedChannelMap(). + * + * Results: + *	None. + * + * Side effects: + *	Deletes the hash table of channels. + * + *---------------------------------------------------------------------- + */ +  static void -ForwardOpToOwnerThread( +DeleteThreadReflectedChannelMap( +    ClientData clientData)	/* The per-thread data structure. */ +{ +    Tcl_HashSearch hSearch;	 /* Search variable. */ +    Tcl_HashEntry *hPtr;	 /* Search variable. */ +    Tcl_ThreadId self = Tcl_GetCurrentThread(); +    ReflectedChannelMap *rcmPtr; /* The map */ +    ForwardingResult *resultPtr; + +    /* +     * The origin thread for one or more reflected channels is gone. +     * NOTE: If this function is called due to a thread getting killed the +     *       per-interp DeleteReflectedChannelMap is apparently not called. +     */ + +    /* +     * Go through the list of pending results and cancel all whose events were +     * destined for this thread. While this is in progress we block any +     * other access to the list of pending results. +     */ + +    Tcl_MutexLock(&rcForwardMutex); + +    for (resultPtr = forwardList; +	    resultPtr != NULL; +	    resultPtr = resultPtr->nextPtr) { +	ForwardingEvent *evPtr; +	ForwardParam *paramPtr; + +	if (resultPtr->dst != self) { +	    /* +	     * Ignore results/events for other threads. +	     */ + +	    continue; +	} + +	/* +	 * The receiver for the event exited, before processing the event. We +	 * detach the result now, wake the originator up and signal failure. +         * +         * Attention: Results may have been detached already, by either the +         * receiver, or this thread, as part of other parts in the thread +         * teardown. Such results are ignored. See ticket [b47b176adf] for the +         * identical race condition in Tcl 8.6 IORTrans. +	 */ + +	evPtr = resultPtr->evPtr; + +	/* Basic crash safety until this routine can get revised [3411310] */ +	if (evPtr == NULL ) { +	    continue; +	} +	paramPtr = evPtr->param; +	if (!evPtr) { +	    continue; +	} + +	evPtr->resultPtr = NULL; +	resultPtr->evPtr = NULL; +	resultPtr->result = TCL_ERROR; + +	ForwardSetStaticError(paramPtr, msg_send_dstlost); + +	Tcl_ConditionNotify(&resultPtr->done); +    } +    Tcl_MutexUnlock(&rcForwardMutex); + +    /* +     * Run over the event queue of this thread and remove all ReflectEvent's +     * still pending. These are inbound events for reflected channels this +     * thread owns but doesn't handle. The inverse of the channel map +     * actually. +     */ + +    Tcl_DeleteEvents(ReflectEventDelete, NULL); + +    /* +     * Get the map of all channels handled by the current thread. This is a +     * ReflectedChannelMap, but on a per-thread basis, not per-interp. Go +     * through the channels, remove all, mark them as dead. +     */ + +    rcmPtr = GetThreadReflectedChannelMap(); +    for (hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch); +	    hPtr != NULL; +	    hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch)) { +	Tcl_Channel chan = Tcl_GetHashValue(hPtr); +	ReflectedChannel *rcPtr = Tcl_GetChannelInstanceData(chan); + +	MarkDead(rcPtr); +	Tcl_DeleteHashEntry(hPtr); +    } +    ckfree(rcmPtr); +} + +static void +ForwardOpToHandlerThread(      ReflectedChannel *rcPtr,	/* Channel instance */      ForwardedOperation op,	/* Forwarded driver operation */ -    CONST VOID *param)		/* Arguments */ +    const void *param)		/* Arguments */  { +    /* +     * Core of the communication from OWNER to HANDLER thread. +     * The receiver is ForwardProc() below. +     */ +      Tcl_ThreadId dst = rcPtr->thread;      ForwardingEvent *evPtr;      ForwardingResult *resultPtr; -    int result; + +    /* +     * We gather the lock early. This allows us to check the liveness of the +     * channel without interference from DeleteThreadReflectedChannelMap(). +     */ + +    Tcl_MutexLock(&rcForwardMutex); + +    if (rcPtr->dead) { +	/* +	 * The channel is marked as dead. Bail out immediately, with an +	 * appropriate error. Do not forget to unlock the mutex on this path. +	 */ + +	ForwardSetStaticError((ForwardParam *) param, msg_send_dstlost); +	Tcl_MutexUnlock(&rcForwardMutex); +	return; +    }      /*       * Create and initialize the event and data structures.       */ -    evPtr = (ForwardingEvent *) ckalloc(sizeof(ForwardingEvent)); -    resultPtr = (ForwardingResult *) ckalloc(sizeof(ForwardingResult)); +    evPtr = ckalloc(sizeof(ForwardingEvent)); +    resultPtr = ckalloc(sizeof(ForwardingResult));      evPtr->event.proc = ForwardProc;      evPtr->resultPtr = resultPtr; @@ -2118,6 +2802,7 @@ ForwardOpToOwnerThread(      resultPtr->src = Tcl_GetCurrentThread();      resultPtr->dst = dst; +    resultPtr->dsti = rcPtr->interp;      resultPtr->done = NULL;      resultPtr->result = -1;      resultPtr->evPtr = evPtr; @@ -2126,41 +2811,47 @@ ForwardOpToOwnerThread(       * Now execute the forward.       */ -    Tcl_MutexLock(&rcForwardMutex);      TclSpliceIn(resultPtr, forwardList); +    /* Do not unlock here. That is done by the ConditionWait */      /* -     * Ensure cleanup of the event if any of the two involved threads exits -     * while this event is pending or in progress. +     * Ensure cleanup of the event if the origin thread exits while this event +     * is pending or in progress. Exit of the destination thread is handled by +     * DeleteThreadReflectedChannelMap(), this is set up by +     * GetThreadReflectedChannelMap(). This is what we use the 'forwardList' +     * (see above) for.       */ -    Tcl_CreateThreadExitHandler(SrcExitProc, (ClientData) evPtr); -    Tcl_CreateThreadExitHandler(DstExitProc, (ClientData) evPtr); +    Tcl_CreateThreadExitHandler(SrcExitProc, evPtr);      /*       * Queue the event and poke the other thread's notifier.       */ -    Tcl_ThreadQueueEvent(dst, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL); +    Tcl_ThreadQueueEvent(dst, (Tcl_Event *) evPtr, TCL_QUEUE_TAIL);      Tcl_ThreadAlert(dst);      /* -     * (*) Block until the other thread has either processed the transfer or +     * (*) Block until the handler thread has either processed the transfer or       * rejected it.       */      while (resultPtr->result < 0) {  	/*  	 * NOTE (1): Is it possible that the current thread goes away while -	 * waiting here? IOW Is it possible that "SrcExitProc" is called -	 * while we are here? See complementary note (2) in "SrcExitProc" +	 * waiting here? IOW Is it possible that "SrcExitProc" is called while +	 * we are here? See complementary note (2) in "SrcExitProc" +	 * +	 * The ConditionWait unlocks the mutex during the wait and relocks it +	 * immediately after.  	 */  	Tcl_ConditionWait(&resultPtr->done, &rcForwardMutex, NULL);      }      /* -     * Unlink result from the forwarder list. +     * Unlink result from the forwarder list. No need to lock. Either still +     * locked, or locked by the ConditionWait       */      TclSpliceOut(resultPtr, forwardList); @@ -2172,17 +2863,15 @@ ForwardOpToOwnerThread(      Tcl_ConditionFinalize(&resultPtr->done);      /* -     * Kill the cleanup handlers now, and the result structure as well, before +     * Kill the cleanup handler now, and the result structure as well, before       * returning the success code.       *       * Note: The event structure has already been deleted.       */ -    Tcl_DeleteThreadExitHandler(SrcExitProc, (ClientData) evPtr); -    Tcl_DeleteThreadExitHandler(DstExitProc, (ClientData) evPtr); +    Tcl_DeleteThreadExitHandler(SrcExitProc, evPtr); -    result = resultPtr->result; -    ckfree((char*) resultPtr); +    ckfree(resultPtr);  }  static int @@ -2191,6 +2880,11 @@ ForwardProc(      int mask)  {      /* +     * HANDLER thread. + +     * The receiver part for the operations coming from the OWNER thread. +     * See ForwardOpToHandlerThread() for the transmitter. +     *       * Notes regarding access to the referenced data.       *       * In principle the data belongs to the originating thread (see @@ -2209,6 +2903,9 @@ ForwardProc(      Tcl_Interp *interp = rcPtr->interp;      ForwardParam *paramPtr = evPtr->param;      Tcl_Obj *resObj = NULL;	/* Interp result of InvokeTclMethod */ +    ReflectedChannelMap *rcmPtr;/* Map of reflected channels with handlers in +                                 * this interp. */ +    Tcl_HashEntry *hPtr;	/* Entry in the above map */      /*       * Ignore the event if no one is waiting for its result anymore. @@ -2229,37 +2926,59 @@ ForwardProc(  	 * call upon for the driver.  	 */ -    case ForwardedClose: +    case ForwardedClose: {  	/*  	 * No parameters/results.  	 */ -	if (InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj) != TCL_OK) { +	if (InvokeTclMethod(rcPtr, METH_FINAL, NULL, NULL, &resObj)!=TCL_OK) {  	    ForwardSetObjError(paramPtr, resObj);  	}  	/* -	 * Freeing is done here, in the origin thread, because the argv[] +	 * Freeing is done here, in the origin thread, callback command  	 * objects belong to this thread. Deallocating them in a different  	 * thread is not allowed +	 * +	 * We remove the channel from both interpreter and thread maps before +	 * releasing the memory, to prevent future accesses (like by +	 * 'postevent') from finding and dereferencing a dangling pointer.  	 */ -	FreeReflectedChannel(rcPtr); +	rcmPtr = GetReflectedChannelMap(interp); +	hPtr = Tcl_FindHashEntry(&rcmPtr->map, +                Tcl_GetChannelName(rcPtr->chan)); +	Tcl_DeleteHashEntry(hPtr); + +	rcmPtr = GetThreadReflectedChannelMap(); +	hPtr = Tcl_FindHashEntry(&rcmPtr->map, +                Tcl_GetChannelName(rcPtr->chan)); +	Tcl_DeleteHashEntry(hPtr); +	MarkDead(rcPtr);  	break; +    }      case ForwardedInput: {  	Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead); +        Tcl_IncrRefCount(toReadObj); -	if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj) != TCL_OK) { -	    ForwardSetObjError(paramPtr, resObj); +        Tcl_Preserve(rcPtr); +	if (InvokeTclMethod(rcPtr, METH_READ, toReadObj, NULL, &resObj)!=TCL_OK){ +	    int code = ErrnoReturn(rcPtr, resObj); + +	    if (code < 0) { +		paramPtr->base.code = code; +	    } else { +		ForwardSetObjError(paramPtr, resObj); +	    }  	    paramPtr->input.toRead = -1;  	} else {  	    /*  	     * Process a regular result.  	     */ -	    int bytec;		/* Number of returned bytes */ -	    unsigned char *bytev; /* Array of returned bytes */ +	    int bytec;			/* Number of returned bytes */ +	    unsigned char *bytev;	/* Array of returned bytes */  	    bytev = Tcl_GetByteArrayFromObj(resObj, &bytec); @@ -2268,20 +2987,30 @@ ForwardProc(  		paramPtr->input.toRead = -1;  	    } else {  		if (bytec > 0) { -		    memcpy(paramPtr->input.buf, bytev, (size_t)bytec); +		    memcpy(paramPtr->input.buf, bytev, (size_t) bytec);  		}  		paramPtr->input.toRead = bytec;  	    }  	} +        Tcl_Release(rcPtr); +        Tcl_DecrRefCount(toReadObj);  	break;      }      case ForwardedOutput: {  	Tcl_Obj *bufObj = Tcl_NewByteArrayObj((unsigned char *) -		paramPtr->output.buf, paramPtr->output.toWrite); +                paramPtr->output.buf, paramPtr->output.toWrite); +        Tcl_IncrRefCount(bufObj); -	if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) { -	    ForwardSetObjError(paramPtr, resObj); +        Tcl_Preserve(rcPtr); +	if (InvokeTclMethod(rcPtr, METH_WRITE, bufObj, NULL, &resObj) != TCL_OK) { +	    int code = ErrnoReturn(rcPtr, resObj); + +	    if (code < 0) { +		paramPtr->base.code = code; +	    } else { +		ForwardSetObjError(paramPtr, resObj); +	    }  	    paramPtr->output.toWrite = -1;  	} else {  	    /* @@ -2291,7 +3020,9 @@ ForwardProc(  	    int written;  	    if (Tcl_GetIntFromObj(interp, resObj, &written) != TCL_OK) { -		ForwardSetObjError(paramPtr, MarshallError(interp)); +		Tcl_DecrRefCount(resObj); +		resObj = MarshallError(interp); +		ForwardSetObjError(paramPtr, resObj);  		paramPtr->output.toWrite = -1;  	    } else if (written==0 || paramPtr->output.toWrite<written) {  		ForwardSetStaticError(paramPtr, msg_write_toomuch); @@ -2300,16 +3031,22 @@ ForwardProc(  		paramPtr->output.toWrite = written;  	    }  	} +        Tcl_Release(rcPtr); +        Tcl_DecrRefCount(bufObj);  	break;      }      case ForwardedSeek: {  	Tcl_Obj *offObj = Tcl_NewWideIntObj(paramPtr->seek.offset);  	Tcl_Obj *baseObj = Tcl_NewStringObj( -		(paramPtr->seek.seekMode==SEEK_SET) ? "start" : -		(paramPtr->seek.seekMode==SEEK_CUR) ? "current" : "end", -1); +                (paramPtr->seek.seekMode==SEEK_SET) ? "start" : +                (paramPtr->seek.seekMode==SEEK_CUR) ? "current" : "end", -1); -	if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj) != TCL_OK) { +        Tcl_IncrRefCount(offObj); +        Tcl_IncrRefCount(baseObj); + +        Tcl_Preserve(rcPtr); +	if (InvokeTclMethod(rcPtr, METH_SEEK, offObj, baseObj, &resObj)!=TCL_OK){  	    ForwardSetObjError(paramPtr, resObj);  	    paramPtr->seek.offset = -1;  	} else { @@ -2328,37 +3065,58 @@ ForwardProc(  		    paramPtr->seek.offset = newLoc;  		}  	    } else { -		ForwardSetObjError(paramPtr, MarshallError(interp)); +		Tcl_DecrRefCount(resObj); +		resObj = MarshallError(interp); +		ForwardSetObjError(paramPtr, resObj);  		paramPtr->seek.offset = -1;  	    }  	} +        Tcl_Release(rcPtr); +        Tcl_DecrRefCount(offObj); +        Tcl_DecrRefCount(baseObj);  	break;      }      case ForwardedWatch: {  	Tcl_Obj *maskObj = DecodeEventMask(paramPtr->watch.mask); +        /* assert maskObj.refCount == 1 */ -	(void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL); +        Tcl_Preserve(rcPtr); +	rcPtr->interest = paramPtr->watch.mask; +	(void) InvokeTclMethod(rcPtr, METH_WATCH, maskObj, NULL, NULL);  	Tcl_DecrRefCount(maskObj); +        Tcl_Release(rcPtr);  	break;      }      case ForwardedBlock: {  	Tcl_Obj *blockObj = Tcl_NewBooleanObj(!paramPtr->block.nonblocking); -	if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj) != TCL_OK) { +        Tcl_IncrRefCount(blockObj); +        Tcl_Preserve(rcPtr); +	if (InvokeTclMethod(rcPtr, METH_BLOCKING, blockObj, NULL, +                &resObj) != TCL_OK) {  	    ForwardSetObjError(paramPtr, resObj);  	} +        Tcl_Release(rcPtr); +        Tcl_DecrRefCount(blockObj);  	break;      }      case ForwardedSetOpt: {  	Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->setOpt.name, -1); -	Tcl_Obj *valueObj = Tcl_NewStringObj(paramPtr->setOpt.value, -1); +	Tcl_Obj *valueObj  = Tcl_NewStringObj(paramPtr->setOpt.value, -1); -	if (InvokeTclMethod(rcPtr, "configure", optionObj, valueObj, &resObj) != TCL_OK) { +        Tcl_IncrRefCount(optionObj); +        Tcl_IncrRefCount(valueObj); +        Tcl_Preserve(rcPtr); +	if (InvokeTclMethod(rcPtr, METH_CONFIGURE, optionObj, valueObj, +                &resObj) != TCL_OK) {  	    ForwardSetObjError(paramPtr, resObj);  	} +        Tcl_Release(rcPtr); +        Tcl_DecrRefCount(optionObj); +        Tcl_DecrRefCount(valueObj);  	break;      } @@ -2369,11 +3127,15 @@ ForwardProc(  	Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->getOpt.name, -1); -	if (InvokeTclMethod(rcPtr, "cget", optionObj, NULL, &resObj) != TCL_OK) { +        Tcl_IncrRefCount(optionObj); +        Tcl_Preserve(rcPtr); +	if (InvokeTclMethod(rcPtr, METH_CGET, optionObj, NULL, &resObj)!=TCL_OK){  	    ForwardSetObjError(paramPtr, resObj);  	} else { -	    Tcl_DStringAppend(paramPtr->getOpt.value, TclGetString(resObj),-1); +	    TclDStringAppendObj(paramPtr->getOpt.value, resObj);  	} +        Tcl_Release(rcPtr); +        Tcl_DecrRefCount(optionObj);  	break;      } @@ -2382,7 +3144,8 @@ ForwardProc(  	 * Retrieve all options.  	 */ -	if (InvokeTclMethod(rcPtr, "cgetall", NULL, NULL, &resObj) != TCL_OK) { +        Tcl_Preserve(rcPtr); +	if (InvokeTclMethod(rcPtr, METH_CGETALL, NULL, NULL, &resObj) != TCL_OK){  	    ForwardSetObjError(paramPtr, resObj);  	} else {  	    /* @@ -2391,11 +3154,13 @@ ForwardProc(  	     */  	    int listc; -	    Tcl_Obj** listv; +	    Tcl_Obj **listv;  	    if (Tcl_ListObjGetElements(interp, resObj, &listc, -		    &listv) != TCL_OK) { -		ForwardSetObjError(paramPtr, MarshallError(interp)); +                    &listv) != TCL_OK) { +		Tcl_DecrRefCount(resObj); +		resObj = MarshallError(interp); +		ForwardSetObjError(paramPtr, resObj);  	    } else if ((listc % 2) == 1) {  		/*  		 * Odd number of elements is wrong. [x]. @@ -2409,20 +3174,22 @@ ForwardProc(  		ForwardSetDynamicError(paramPtr, buf);  	    } else {  		int len; -		CONST char *str = Tcl_GetStringFromObj(resObj, &len); +		const char *str = TclGetStringFromObj(resObj, &len);  		if (len) { -		    Tcl_DStringAppend(paramPtr->getOpt.value, " ", 1); +		    TclDStringAppendLiteral(paramPtr->getOpt.value, " ");  		    Tcl_DStringAppend(paramPtr->getOpt.value, str, len);  		}  	    }  	} +        Tcl_Release(rcPtr);  	break;      default:  	/*  	 * Bad operation code.  	 */ +  	Tcl_Panic("Bad operation code in ForwardProc");  	break;      } @@ -2456,7 +3223,7 @@ static void  SrcExitProc(      ClientData clientData)  { -    ForwardingEvent *evPtr = (ForwardingEvent *) clientData; +    ForwardingEvent *evPtr = clientData;      ForwardingResult *resultPtr;      ForwardParam *paramPtr; @@ -2501,42 +3268,15 @@ SrcExitProc(  }  static void -DstExitProc( -    ClientData clientData) -{ -    ForwardingEvent *evPtr = (ForwardingEvent *) clientData; -    ForwardingResult *resultPtr = evPtr->resultPtr; -    ForwardParam *paramPtr = evPtr->param; - -    /* -     * NOTE (3): It is not clear if the event still exists when this handler -     * is called. We might have to use 'resultPtr' as our clientData instead. -     */ - -    /* -     * The receiver for the event exited, before processing the event. We -     * detach the result now, wake the originator up and signal failure. -     */ - -    evPtr->resultPtr = NULL; -    resultPtr->evPtr = NULL; -    resultPtr->result = TCL_ERROR; - -    ForwardSetStaticError(paramPtr, msg_send_dstlost); - -    Tcl_ConditionNotify(&resultPtr->done); -} - -static void  ForwardSetObjError(      ForwardParam *paramPtr,      Tcl_Obj *obj)  {      int len; -    CONST char *msgStr = Tcl_GetStringFromObj(obj, &len); +    const char *msgStr = TclGetStringFromObj(obj, &len);      len++; -    ForwardSetDynamicError(paramPtr, ckalloc((unsigned) len)); +    ForwardSetDynamicError(paramPtr, ckalloc(len));      memcpy(paramPtr->base.msgStr, msgStr, (unsigned) len);  }  #endif @@ -2546,5 +3286,7 @@ ForwardSetObjError(   * mode: c   * c-basic-offset: 4   * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil   * End:   */ | 
