summaryrefslogtreecommitdiffstats
path: root/generic/tclIORChan.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclIORChan.c')
-rw-r--r--generic/tclIORChan.c1673
1 files changed, 1187 insertions, 486 deletions
diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c
index faaaca1..94428bb 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.12 2005/12/13 22:43:17 kennykb 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,24 +416,26 @@ 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,
- Tcl_Obj *argTwoObj, Tcl_Obj **resultObjPtr,
- int flags);
+ MethodName method, Tcl_Obj *argOneObj,
+ Tcl_Obj *argTwoObj, Tcl_Obj **resultObjPtr);
-#define INVOKE_NO_CAPTURE 0x01
+static ReflectedChannelMap * GetReflectedChannelMap(Tcl_Interp *interp);
+static void DeleteReflectedChannelMap(ClientData clientData,
+ Tcl_Interp *interp);
+static int ErrnoReturn(ReflectedChannel *rcPtr, Tcl_Obj *resObj);
/*
* Global constant strings (messages). ==================
@@ -411,16 +444,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}";
+static const char *msg_send_dstlost = "{Owner lost}";
#endif /* TCL_THREADS */
-
+static const char *msg_dstlost = "-code 1 -level 0 -errorcode NONE -errorinfo {} -errorline 1 {Owner lost}";
+
/*
* Main methods to plug into the 'chan' ensemble'. ==================
*/
@@ -448,7 +481,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 */
@@ -465,6 +498,12 @@ TclChanCreateObjCmd(
Tcl_Obj *resObj; /* Result data for 'initialize' */
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
@@ -498,7 +537,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.
*/
@@ -519,10 +558,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.
@@ -535,14 +570,12 @@ TclChanCreateObjCmd(
*/
modeObj = DecodeEventMask(mode);
- result = InvokeTclMethod(rcPtr, "initialize", modeObj, NULL, &resObj,
- INVOKE_NO_CAPTURE);
+ /* assert modeObj.refCount == 1 */
+ result = InvokeTclMethod(rcPtr, METH_INIT, modeObj, NULL, &resObj);
Tcl_DecrRefCount(modeObj);
- if (result != TCL_OK) {
- Tcl_Obj *err = Tcl_NewStringObj("Initialize failure: ", -1);
- Tcl_AppendObjToObj(err, resObj);
- Tcl_SetObjResult(interp, err);
+ if (result != TCL_OK) {
+ UnmarshallErrorResult(interp, resObj);
Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */
goto error;
}
@@ -554,18 +587,11 @@ TclChanCreateObjCmd(
* Compare open mode against optional r/w.
*/
- Tcl_AppendResult(interp, "Initialize failure: ", NULL);
-
- if (Tcl_ListObjGetElements(interp, resObj, &listc, &listv) != TCL_OK) {
- /*
- * The function above replaces my prefix in case of an error, so more
- * work for us to get the prefix back into the error message
- */
-
- Tcl_Obj *err = Tcl_NewStringObj("Initialize failure: ", -1);
-
- Tcl_AppendObjToObj(err, Tcl_GetObjResult(interp));
- Tcl_SetObjResult(interp, err);
+ if (Tcl_ListObjGetElements(NULL, resObj, &listc, &listv) != TCL_OK) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "chan handler \"%s initialize\" returned non-list: %s",
+ Tcl_GetString(cmdObj), Tcl_GetString(resObj)));
+ Tcl_DecrRefCount(resObj);
goto error;
}
@@ -573,41 +599,52 @@ TclChanCreateObjCmd(
while (listc > 0) {
if (Tcl_GetIndexFromObj(interp, listv[listc-1], methodNames,
"method", TCL_EXACT, &methIndex) != TCL_OK) {
- Tcl_Obj *err = Tcl_NewStringObj("Initialize failure: ", -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) {
- Tcl_AppendResult(interp, "Not all required methods supported", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "chan handler \"%s\" does not support all required methods",
+ Tcl_GetString(cmdObj)));
goto error;
}
if ((mode & TCL_READABLE) && !HAS(methods, METH_READ)) {
- Tcl_AppendResult(interp, "Reading not supported, but requested", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "chan handler \"%s\" lacks a \"read\" method",
+ Tcl_GetString(cmdObj)));
goto error;
}
if ((mode & TCL_WRITABLE) && !HAS(methods, METH_WRITE)) {
- Tcl_AppendResult(interp, "Writing not supported, but requested", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "chan handler \"%s\" lacks a \"write\" method",
+ Tcl_GetString(cmdObj)));
goto error;
}
if (!IMPLIES(HAS(methods, METH_CGET), HAS(methods, METH_CGETALL))) {
- Tcl_AppendResult(interp,
- "'cgetall' not supported, but should be, as 'cget' is", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "chan handler \"%s\" supports \"cget\" but not \"cgetall\"",
+ Tcl_GetString(cmdObj)));
goto error;
}
if (!IMPLIES(HAS(methods, METH_CGETALL), HAS(methods, METH_CGET))) {
- Tcl_AppendResult(interp,
- "'cget' not supported, but should be, as 'cgetall' is", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "chan handler \"%s\" supports \"cgetall\" but not \"cget\"",
+ Tcl_GetString(cmdObj)));
goto error;
}
@@ -617,7 +654,11 @@ TclChanCreateObjCmd(
* Everything is fine now.
*/
- rcPtr->methods = methods;
+ chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr,
+ mode);
+ rcPtr->chan = chan;
+ Tcl_Preserve(chan);
+ chanPtr = (Channel *) chan;
if ((methods & NULLABLE_METHODS) != NULLABLE_METHODS) {
/*
@@ -626,8 +667,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));
@@ -649,21 +689,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
@@ -688,14 +747,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]
*
@@ -708,13 +813,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...
@@ -731,12 +838,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);
/*
@@ -749,17 +878,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");
}
/*
@@ -776,8 +901,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;
}
@@ -785,7 +911,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.
@@ -802,7 +965,7 @@ TclChanPostEventObjCmd(
* Channel error message marshalling utilities.
*/
-static Tcl_Obj*
+static Tcl_Obj *
MarshallError(
Tcl_Interp *interp)
{
@@ -845,6 +1008,9 @@ UnmarshallErrorResult(
if (Tcl_ListObjGetElements(interp, msgObj, &lc, &lv) != TCL_OK) {
Tcl_Panic("TclChanCaughtErrorBypass: Bad syntax of caught result");
}
+ if (interp == NULL) {
+ return;
+ }
explicitResult = lc & 1; /* Odd number of values? */
numOptions = lc - explicitResult;
@@ -854,6 +1020,7 @@ UnmarshallErrorResult(
}
(void) Tcl_SetReturnOptions(interp, Tcl_NewListObj(numOptions, lv));
+ ((Interp *) interp)->flags &= ~ERR_ALREADY_LOGGED;
}
int
@@ -938,11 +1105,14 @@ 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 */
- if (interp == NULL) {
+ if (TclInThreadExit()) {
/*
* This call comes from TclFinalizeIOSystem. There are no
* interpreters, and therefore we cannot call upon the handler command
@@ -954,21 +1124,23 @@ 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);
@@ -977,19 +1149,7 @@ ReflectClose(
}
#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);
+ Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
return EOK;
}
@@ -1001,28 +1161,60 @@ 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, 0);
+ 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
+ rcmPtr = GetThreadReflectedChannelMap();
+ hPtr = Tcl_FindHashEntry(&rcmPtr->map,
+ Tcl_GetChannelName(rcPtr->chan));
+ if (hPtr) {
+ Tcl_DeleteHashEntry(hPtr);
+ }
+#endif
+
+ Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
#ifdef TCL_THREADS
- FreeReflectedChannel(rcPtr);
}
#endif
return (result == TCL_OK) ? EOK : EINVAL;
@@ -1051,25 +1243,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?
*/
@@ -1080,11 +1260,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;
}
@@ -1096,31 +1282,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, 0)!=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;
}
/*
@@ -1142,28 +1343,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?
*/
@@ -1174,11 +1363,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;
}
@@ -1190,24 +1385,38 @@ ReflectOutput(
/* ASSERT: rcPtr->method & FLAG(METH_WRITE) */
/* ASSERT: rcPtr->mode & TCL_WRITABLE */
+ Tcl_Preserve(rcPtr);
+
bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite);
- if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj, 0) != 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_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
@@ -1215,12 +1424,20 @@ 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);
return written;
+ invalid:
+ *errorCodePtr = EINVAL;
+ error:
+ written = -1;
+ goto stop;
}
/*
@@ -1246,9 +1463,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;
@@ -1263,11 +1479,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;
}
@@ -1278,33 +1495,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, 0)!=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
@@ -1347,11 +1572,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.
@@ -1379,7 +1602,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
@@ -1390,10 +1613,14 @@ ReflectWatch(
}
#endif
+ Tcl_Preserve(rcPtr);
+
maskObj = DecodeEventMask(mask);
- (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL,
- INVOKE_NO_CAPTURE);
+ /* assert maskObj.refCount == 1 */
+ (void) InvokeTclMethod(rcPtr, METH_WATCH, maskObj, NULL, NULL);
Tcl_DecrRefCount(maskObj);
+
+ Tcl_Release(rcPtr);
}
/*
@@ -1418,7 +1645,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' */
@@ -1433,7 +1660,7 @@ ReflectBlock(
p.block.nonblocking = nonblocking;
- ForwardOpToOwnerThread(rcPtr, ForwardedBlock, &p);
+ ForwardOpToHandlerThread(rcPtr, ForwardedBlock, &p);
if (p.base.code != TCL_OK) {
PassReceivedError(rcPtr->chan, &p);
@@ -1445,19 +1672,62 @@ ReflectBlock(
#endif
blockObj = Tcl_NewBooleanObj(!nonblocking);
+ Tcl_IncrRefCount(blockObj);
- if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj,
- 0) != TCL_OK) {
+ Tcl_Preserve(rcPtr);
+
+ 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
/*
*----------------------------------------------------------------------
*
@@ -1478,12 +1748,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' */
@@ -1498,7 +1767,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);
@@ -1511,15 +1780,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,0);
+
+ 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;
}
@@ -1543,7 +1820,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 */
{
/*
@@ -1551,12 +1828,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?
@@ -1576,7 +1853,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);
@@ -1595,21 +1872,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);
}
- if (InvokeTclMethod(rcPtr, method, optionObj, NULL, &resObj, 0)!=TCL_OK) {
+ 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;
}
/*
@@ -1618,9 +1897,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;
}
/*
@@ -1635,8 +1913,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) {
@@ -1644,26 +1921,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 = Tcl_GetStringFromObj(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;
}
/*
@@ -1676,9 +1962,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:
@@ -1695,7 +1981,7 @@ ReflectGetOption(
static int
EncodeEventMask(
Tcl_Interp *interp,
- CONST char *objName,
+ const char *objName,
Tcl_Obj *obj,
int *mask)
{
@@ -1710,7 +1996,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;
}
@@ -1743,7 +2030,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.
*
@@ -1757,7 +2044,7 @@ static Tcl_Obj *
DecodeEventMask(
int mask)
{
- register CONST char *eventStr;
+ register const char *eventStr;
Tcl_Obj *evObj;
switch (mask & RANDW) {
@@ -1777,6 +2064,7 @@ DecodeEventMask(
evObj = Tcl_NewStringObj(eventStr, -1);
Tcl_IncrRefCount(evObj);
+ /* assert evObj.refCount == 1 */
return evObj;
}
@@ -1805,67 +2093,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;
}
@@ -1903,9 +2156,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);
@@ -1913,27 +2165,24 @@ 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);
- }
-
- n = rcPtr->argc - 2;
- for (i=0; i<n; i++) {
- Tcl_DecrRefCount(rcPtr->argv[i]);
+ ckfree(chanPtr->typePtr);
+ chanPtr->typePtr = NULL;
}
-
- ckfree((char*) rcPtr->argv);
- ckfree((char*) rcPtr);
+ Tcl_Release(chanPtr);
+ Tcl_DecrRefCount(rcPtr->name);
+ Tcl_DecrRefCount(rcPtr->methods);
+ Tcl_DecrRefCount(rcPtr->cmd);
+ ckfree(rcPtr);
}
/*
@@ -1952,53 +2201,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 flags)
+ 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);
}
}
@@ -2007,9 +2274,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
@@ -2018,7 +2286,7 @@ InvokeTclMethod(
*/
if (resultObjPtr) {
- if ((result == TCL_OK) || (flags & INVOKE_NO_CAPTURE)) {
+ if (result == TCL_OK) {
/*
* Ok result taken as is, also if the caller requests that there
* is no capture.
@@ -2029,29 +2297,36 @@ InvokeTclMethod(
/*
* Non-ok result is always treated as an error. We have to capture
* the full state of the result, including additional options.
+ *
+ * This is complex and ugly, and would be completely unnecessary
+ * if we only added support for a TCL_FORBID_EXCEPTIONS flag.
*/
- result = TCL_ERROR;
+ if (result != TCL_ERROR) {
+ int cmdLen;
+ const char *cmdString = Tcl_GetStringFromObj(cmd, &cmdLen);
+
+ Tcl_IncrRefCount(cmd);
+ Tcl_ResetResult(rcPtr->interp);
+ 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;
+ }
+ 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).
@@ -2069,24 +2344,402 @@ 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", Tcl_GetString(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
+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);
+
+ rcPtr->dead = 1;
+ 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.
+ */
+
+ evPtr = resultPtr->evPtr;
+
+ /* Basic crash safety until this routine can get revised [3411310] */
+ if (evPtr == NULL) {
+ continue;
+ }
+ paramPtr = evPtr->param;
+
+ 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;
+ }
+
+ rcPtr->dead = 1;
+ 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.
+ */
+
+ evPtr = resultPtr->evPtr;
+
+ /* Basic crash safety until this routine can get revised [3411310] */
+ if (evPtr == NULL ) {
+ continue;
+ }
+ paramPtr = evPtr->param;
+
+ 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);
+
+ rcPtr->dead = 1;
+ 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;
@@ -2096,6 +2749,7 @@ ForwardOpToOwnerThread(
resultPtr->src = Tcl_GetCurrentThread();
resultPtr->dst = dst;
+ resultPtr->dsti = rcPtr->interp;
resultPtr->done = NULL;
resultPtr->result = -1;
resultPtr->evPtr = evPtr;
@@ -2104,41 +2758,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);
@@ -2150,17 +2810,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
@@ -2169,6 +2827,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
@@ -2187,6 +2850,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.
@@ -2212,34 +2878,54 @@ ForwardProc(
* No parameters/results.
*/
- if (InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj,
- 0) != 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);
+
+ Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
break;
case ForwardedInput: {
Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead);
+ Tcl_IncrRefCount(toReadObj);
- if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj,
- 0) != 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);
@@ -2248,21 +2934,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,
- 0) != 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 {
/*
@@ -2272,7 +2967,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);
@@ -2281,17 +2978,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,
- 0) != 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 {
@@ -2310,40 +3012,57 @@ 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,
- INVOKE_NO_CAPTURE);
+ Tcl_Preserve(rcPtr);
+ (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,
- 0) != 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,
- 0) != 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;
}
@@ -2354,12 +3073,15 @@ ForwardProc(
Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->getOpt.name, -1);
- if (InvokeTclMethod(rcPtr, "cget", optionObj, NULL, &resObj,
- 0) != 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;
}
@@ -2368,8 +3090,8 @@ ForwardProc(
* Retrieve all options.
*/
- if (InvokeTclMethod(rcPtr, "cgetall", NULL, NULL, &resObj,
- 0) != TCL_OK) {
+ Tcl_Preserve(rcPtr);
+ if (InvokeTclMethod(rcPtr, METH_CGETALL, NULL, NULL, &resObj) != TCL_OK){
ForwardSetObjError(paramPtr, resObj);
} else {
/*
@@ -2378,11 +3100,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].
@@ -2396,20 +3120,22 @@ ForwardProc(
ForwardSetDynamicError(paramPtr, buf);
} else {
int len;
- CONST char *str = Tcl_GetStringFromObj(resObj, &len);
+ const char *str = Tcl_GetStringFromObj(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;
}
@@ -2443,7 +3169,7 @@ static void
SrcExitProc(
ClientData clientData)
{
- ForwardingEvent *evPtr = (ForwardingEvent *) clientData;
+ ForwardingEvent *evPtr = clientData;
ForwardingResult *resultPtr;
ForwardParam *paramPtr;
@@ -2488,42 +3214,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 = Tcl_GetStringFromObj(obj, &len);
len++;
- ForwardSetDynamicError(paramPtr, ckalloc((unsigned) len));
+ ForwardSetDynamicError(paramPtr, ckalloc(len));
memcpy(paramPtr->base.msgStr, msgStr, (unsigned) len);
}
#endif
@@ -2533,5 +3232,7 @@ ForwardSetObjError(
* mode: c
* c-basic-offset: 4
* fill-column: 78
+ * tab-width: 8
+ * indent-tabs-mode: nil
* End:
*/