Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix [18f4a94d03] by backing out [9bcec7cd880540c3] (again) |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | bug-18f4a94d03 |
Files: | files | file ages | folders |
SHA3-256: |
a2633b5cc5094b959826dd146d78eb20 |
User & Date: | jan.nijtmans 2024-04-18 15:32:16 |
Context
2024-04-18
| ||
18:34 | Rebase to 9.0 check-in: d42b38aa43 user: jan.nijtmans tags: bug-18f4a94d03, core-bug-18f4a94d03 | |
15:32 | Fix [18f4a94d03] by backing out [9bcec7cd880540c3] (again) check-in: a2633b5cc5 user: jan.nijtmans tags: bug-18f4a94d03 | |
15:22 | Merge 8.6 check-in: 3b1ba17e3a user: jan.nijtmans tags: core-8-branch | |
Changes
Changes to generic/tclIO.c.
︙ | ︙ | |||
163 164 165 166 167 168 169 | static int CheckChannelErrors(ChannelState *statePtr, int direction); static int CheckForDeadChannel(Tcl_Interp *interp, ChannelState *statePtr); static void CheckForStdChannelsBeingClosed(Tcl_Channel chan); static void CleanupChannelHandlers(Tcl_Interp *interp, Channel *chanPtr); | < | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | static int CheckChannelErrors(ChannelState *statePtr, int direction); static int CheckForDeadChannel(Tcl_Interp *interp, ChannelState *statePtr); static void CheckForStdChannelsBeingClosed(Tcl_Channel chan); static void CleanupChannelHandlers(Tcl_Interp *interp, Channel *chanPtr); static int CloseChannel(Tcl_Interp *interp, Channel *chanPtr, int errorCode); static int CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr, int errorCode, int flags); static int CloseWrite(Tcl_Interp *interp, Channel *chanPtr); static void CommonGetsCleanup(Channel *chanPtr); static int CopyData(CopyState *csPtr, int mask); |
︙ | ︙ | |||
3190 3191 3192 3193 3194 3195 3196 | Tcl_SetErrno(errorCode); } } /* * Cancel any outstanding timer. */ | < > | 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 | Tcl_SetErrno(errorCode); } } /* * Cancel any outstanding timer. */ DeleteTimerHandler(statePtr); /* * Mark the channel as deleted by clearing the type structure. */ if (chanPtr->downChanPtr != NULL) { Channel *downChanPtr = chanPtr->downChanPtr; |
︙ | ︙ | |||
3540 3541 3542 3543 3544 3545 3546 | TclDecrRefCount(statePtr->chanMsg); statePtr->chanMsg = NULL; } } Tcl_ClearChannelHandlers(chan); | < < < < < | 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 | TclDecrRefCount(statePtr->chanMsg); statePtr->chanMsg = NULL; } } Tcl_ClearChannelHandlers(chan); /* * Invoke the registered close callbacks and delete their records. */ while (statePtr->closeCbPtr != NULL) { cbPtr = statePtr->closeCbPtr; statePtr->closeCbPtr = cbPtr->nextPtr; |
︙ | ︙ | |||
7622 7623 7624 7625 7626 7627 7628 | if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR)) { return 0; } return GotFlag(statePtr, CHANNEL_EOF) ? 1 : 0; } | < | 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 | if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR)) { return 0; } return GotFlag(statePtr, CHANNEL_EOF) ? 1 : 0; } /* *---------------------------------------------------------------------- * * TclChannelGetBlockingMode -- * * Returns 1 if the channel is in blocking mode (default), 0 otherwise. * |
︙ | ︙ | |||
7648 7649 7650 7651 7652 7653 7654 | Tcl_Channel chan) { ChannelState *statePtr = ((Channel *) chan)->state; /* State of real channel structure. */ return GotFlag(statePtr, CHANNEL_NONBLOCKING) ? 0 : 1; } | | | 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 | Tcl_Channel chan) { ChannelState *statePtr = ((Channel *) chan)->state; /* State of real channel structure. */ return GotFlag(statePtr, CHANNEL_NONBLOCKING) ? 0 : 1; } /* *---------------------------------------------------------------------- * * Tcl_InputBlocked -- * * Returns 1 if input is blocked on this channel, 0 otherwise. * |
︙ | ︙ | |||
8756 8757 8758 8759 8760 8761 8762 | static void UpdateInterest( Channel *chanPtr) /* Channel to update. */ { ChannelState *statePtr = chanPtr->state; /* State info for channel */ | < | 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 | static void UpdateInterest( Channel *chanPtr) /* Channel to update. */ { ChannelState *statePtr = chanPtr->state; /* State info for channel */ int mask = statePtr->interestMask; if (chanPtr->typePtr == NULL) { /* Do not update interest on a closed channel */ return; } |
︙ | ︙ | |||
8834 8835 8836 8837 8838 8839 8840 | TclChannelPreserve((Tcl_Channel)chanPtr); statePtr->timerChanPtr = chanPtr; statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc, chanPtr); } } } | < < < < < < < < < < < < < < | 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 | TclChannelPreserve((Tcl_Channel)chanPtr); statePtr->timerChanPtr = chanPtr; statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc, chanPtr); } } } ChanWatch(chanPtr, mask); } /* *---------------------------------------------------------------------- * * ChannelTimerProc -- |
︙ | ︙ | |||
8876 8877 8878 8879 8880 8881 8882 | ChannelTimerProc( void *clientData) { Channel *chanPtr = (Channel *)clientData; /* State info for channel */ ChannelState *statePtr = chanPtr->state; | < < < < < < | > > | < | | | < < < < < < < < < < < < | < < | > | | | < < > | | < < < < < < < | < | < | > | 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 | ChannelTimerProc( void *clientData) { Channel *chanPtr = (Channel *)clientData; /* State info for channel */ ChannelState *statePtr = chanPtr->state; if (chanPtr->typePtr == NULL) { statePtr->timer = NULL; TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr); statePtr->timerChanPtr = NULL; } else { if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA) && (statePtr->interestMask & TCL_READABLE) && (statePtr->inQueueHead != NULL) && IsBufferReady(statePtr->inQueueHead)) { /* * Restart the timer in case a channel handler reenters the event loop * before UpdateInterest gets called by Tcl_NotifyChannel. */ statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc,chanPtr); Tcl_Preserve(statePtr); Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE); Tcl_Release(statePtr); } else { statePtr->timer = NULL; UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr); statePtr->timerChanPtr = NULL; } } } static void DeleteTimerHandler( ChannelState *statePtr ) { if (statePtr->timer != NULL) { Tcl_DeleteTimerHandler(statePtr->timer); statePtr->timer = NULL; TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr); statePtr->timerChanPtr = NULL; } } /* *---------------------------------------------------------------------- * * Tcl_CreateChannelHandler -- * |
︙ | ︙ |
Changes to generic/tclIORChan.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 | Tcl_Interp *interp, const char *optionName, Tcl_DString *dsPtr); static int ReflectSetOption(void *clientData, Tcl_Interp *interp, const char *optionName, const char *newValue); static int ReflectTruncate(void *clientData, long long length); | < < | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | Tcl_Interp *interp, const char *optionName, Tcl_DString *dsPtr); static int ReflectSetOption(void *clientData, Tcl_Interp *interp, const char *optionName, const char *newValue); static int ReflectTruncate(void *clientData, long long length); /* * The C layer channel type/driver definition used by the reflection. */ static const Tcl_ChannelType tclRChannelType = { "tclrchannel", /* Type name. */ |
︙ | ︙ | |||
117 118 119 120 121 122 123 | 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. */ | < < < < < < < < < < < > > | > | < | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 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. * * Most channel implementations need a timer in the C level to ensure that * data in buffers is flushed out through the generation of fake file * events. * * See 'refchan', 'memchan', etc. * * Here this is _not_ required. Interest in events is posted to the Tcl * level via 'watch'. And posting of events is possible from the Tcl level * as well, via 'chan postevent'. This means that the generation of all * events, fake or not, timer based or not, is completely in the hands of * the Tcl level. Therefore no timer here. */ } ReflectedChannel; /* * Structure of the table mapping 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' |
︙ | ︙ | |||
955 956 957 958 959 960 961 | /* * We have the channel and the events to post. */ #if TCL_THREADS if (rcPtr->owner == rcPtr->thread) { #endif | | < < < < < < < < < < < | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 | /* * We have the channel and the events to post. */ #if TCL_THREADS if (rcPtr->owner == rcPtr->thread) { #endif Tcl_NotifyChannel(chan, events); #if TCL_THREADS } else { ReflectEvent *ev = (ReflectEvent *)ckalloc(sizeof(ReflectEvent)); ev->header.proc = ReflectEventRun; ev->events = events; ev->rcPtr = rcPtr; |
︙ | ︙ | |||
1013 1014 1015 1016 1017 1018 1019 | Tcl_ResetResult(interp); return TCL_OK; #undef CHAN #undef EVENT } | < < < < < < < < < < < < < < < < < < | 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 | Tcl_ResetResult(interp); return TCL_OK; #undef CHAN #undef EVENT } /* * Channel error message marshalling utilities. */ static Tcl_Obj * MarshallError( |
︙ | ︙ | |||
1230 1231 1232 1233 1234 1235 1236 | #endif tctPtr = ((Channel *)rcPtr->chan)->typePtr; if (tctPtr && tctPtr != &tclRChannelType) { ckfree(tctPtr); ((Channel *)rcPtr->chan)->typePtr = NULL; } | < < < < < < | 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 | #endif tctPtr = ((Channel *)rcPtr->chan)->typePtr; if (tctPtr && tctPtr != &tclRChannelType) { ckfree(tctPtr); ((Channel *)rcPtr->chan)->typePtr = NULL; } Tcl_EventuallyFree(rcPtr, FreeReflectedChannel); return EOK; } /* * Are we in the correct thread? */ |
︙ | ︙ | |||
1305 1306 1307 1308 1309 1310 1311 | } #endif tctPtr = ((Channel *)rcPtr->chan)->typePtr; if (tctPtr && tctPtr != &tclRChannelType) { ckfree(tctPtr); ((Channel *)rcPtr->chan)->typePtr = NULL; } | < < < < < < | 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 | } #endif tctPtr = ((Channel *)rcPtr->chan)->typePtr; if (tctPtr && tctPtr != &tclRChannelType) { ckfree(tctPtr); ((Channel *)rcPtr->chan)->typePtr = NULL; } Tcl_EventuallyFree(rcPtr, FreeReflectedChannel); return (result == TCL_OK) ? EOK : EINVAL; } /* *---------------------------------------------------------------------- * |
︙ | ︙ | |||
2276 2277 2278 2279 2280 2281 2282 | rcPtr = (ReflectedChannel *)ckalloc(sizeof(ReflectedChannel)); /* rcPtr->chan: Assigned by caller. Dummy data here. */ rcPtr->chan = NULL; rcPtr->interp = interp; rcPtr->dead = 0; | < < | 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 | rcPtr = (ReflectedChannel *)ckalloc(sizeof(ReflectedChannel)); /* rcPtr->chan: Assigned by caller. Dummy data here. */ rcPtr->chan = NULL; rcPtr->interp = interp; rcPtr->dead = 0; #if TCL_THREADS rcPtr->thread = Tcl_GetCurrentThread(); #endif rcPtr->mode = mode; rcPtr->interest = 0; /* Initially no interest registered */ rcPtr->cmd = TclListObjCopy(NULL, cmdpfxObj); |
︙ | ︙ |
Changes to tests/ioCmd.test.
︙ | ︙ | |||
981 982 983 984 985 986 987 | return -code return $args } proc onfinal {} { upvar args hargs if {[lindex $hargs 0] ne "finalize"} {return} return -code return "" } | < < < < < < < < < < < | 981 982 983 984 985 986 987 988 989 990 991 992 993 994 | return -code return $args } proc onfinal {} { upvar args hargs if {[lindex $hargs 0] ne "finalize"} {return} return -code return "" } } # Set everything up in the main thread. eval $helperscript # --- --- --- --------- --------- --------- # method finalize |
︙ | ︙ | |||
2073 2074 2075 2076 2077 2078 2079 | set stop [after 15000 {lappend res TIMEOUT; set tock 1}] after 1000 {note [chan postevent $c r]} vwait ::tock catch {after cancel $stop} close $c rename foo {} set res | | | < < < < < < < < < < < < < < < < < < < < < < < < < | 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 | set stop [after 15000 {lappend res TIMEOUT; set tock 1}] after 1000 {note [chan postevent $c r]} vwait ::tock catch {after cancel $stop} close $c rename foo {} set res } -result {{watch rc* read} {} TOCK {} {watch rc* {}}} test iocmd-31.7 {chan postevent, posted events do happen} -match glob -body { set res {} proc foo {args} {oninit; onfinal; track; return} set c [chan create {r w} foo] note [fileevent $c writable {lappend res TOCK; set tock 1}] set stop [after 15000 {lappend res TIMEOUT; set tock 1}] after 1000 {note [chan postevent $c w]} vwait ::tock catch {after cancel $stop} close $c rename foo {} set res } -result {{watch rc* write} {} TOCK {} {watch rc* {}}} test iocmd-31.8 {chan postevent after close throws error} -match glob -setup { proc foo {args} {oninit; onfinal; track; return} proc dummy args { return } set c [chan create {r w} foo] fileevent $c readable dummy } -body { close $c chan postevent $c read } -cleanup { rename foo {} rename dummy {} } -returnCodes error -result {can not find reflected channel named "rc*"} # --- === *** ########################### # 'Pull the rug' tests. Create channel in a interpreter A, move to # other interpreter B, destroy the origin interpreter (A) before or # during access from B. Must not crash, must return proper errors. test iocmd-32.0 {origin interpreter of moved channel gone} -match glob -body { |
︙ | ︙ |