Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix [18f4a94d03], by reverting [9bcec7cd880540c3], which caused it. See here for motivation, approved by the TCT. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | core-8-branch |
Files: | files | file ages | folders |
SHA3-256: |
8d4d978bd3ca580d8e6be3fd3c1bec1b |
User & Date: | jan.nijtmans 2024-05-29 09:44:42.357 |
Context
2024-05-29
| ||
19:27 | Merge-mark check-in: e3d0608d3f user: jan.nijtmans tags: core-8-branch | |
15:20 | Let's [fix] the %p/%z/%t type specifiers, so they behave like C in scripts, and document ... check-in: 2d6520b382 user: jan.nijtmans tags: bug-9c258a841a | |
09:45 | Merge-mark 8.7 check-in: 95f9e8176e user: jan.nijtmans tags: trunk, main | |
09:44 | Fix [18f4a94d03], by reverting [9bcec7cd880540c3], which caused it. See [https://core.tcl-lang.org/t... check-in: 8d4d978bd3 user: jan.nijtmans tags: core-8-branch | |
08:37 | Merge trunk Closed-Leaf check-in: 0258b07434 user: apnadkarni tags: bug-18f4a94d03 | |
2024-05-28
| ||
13:04 | merge 8.6 check-in: af3c128935 user: sebres tags: core-8-branch | |
Changes
Changes to generic/tclIO.c.
︙ | ︙ | |||
164 165 166 167 168 169 170 | 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); | < | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | 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); |
︙ | ︙ | |||
3570 3571 3572 3573 3574 3575 3576 | TclDecrRefCount(statePtr->chanMsg); statePtr->chanMsg = NULL; } } Tcl_ClearChannelHandlers(chan); | < < < < < | 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 | 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; |
︙ | ︙ | |||
8789 8790 8791 8792 8793 8794 8795 | static void UpdateInterest( Channel *chanPtr) /* Channel to update. */ { ChannelState *statePtr = chanPtr->state; /* State info for channel */ | < | 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 | 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; } |
︙ | ︙ | |||
8867 8868 8869 8870 8871 8872 8873 | TclChannelPreserve((Tcl_Channel)chanPtr); statePtr->timerChanPtr = chanPtr; statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc, chanPtr); } } } | < < < < < < < < < < < < < < | 8860 8861 8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 | TclChannelPreserve((Tcl_Channel)chanPtr); statePtr->timerChanPtr = chanPtr; statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc, chanPtr); } } } ChanWatch(chanPtr, mask); } /* *---------------------------------------------------------------------- * * ChannelTimerProc -- |
︙ | ︙ | |||
8909 8910 8911 8912 8913 8914 8915 | ChannelTimerProc( void *clientData) { Channel *chanPtr = (Channel *)clientData; /* State info for channel */ ChannelState *statePtr = chanPtr->state; | < < < < < < | > > | < | | | < < < < < < < < < < < < | < < | > | | | < < > | | < < | < < < < < < | < | < | > | 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 | 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 -- * |
︙ | ︙ | |||
9953 9954 9955 9956 9957 9958 9959 | * Read up to bufSize bytes. */ if ((csPtr->toRead == -1) || (csPtr->toRead > (Tcl_WideInt)csPtr->bufSize)) { sizeb = csPtr->bufSize; } else { | | | 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 | * Read up to bufSize bytes. */ if ((csPtr->toRead == -1) || (csPtr->toRead > (Tcl_WideInt)csPtr->bufSize)) { sizeb = csPtr->bufSize; } else { sizeb = csPtr->toRead; } if (inBinary || sameEncoding) { size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb, !GotFlag(inStatePtr, CHANNEL_NONBLOCKING)); } else { size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb, |
︙ | ︙ | |||
10302 10303 10304 10305 10306 10307 10308 | /* * Don't read more data if we have what we need. */ while (!bufPtr || /* We got no buffer! OR */ (!IsBufferFull(bufPtr) && /* Our buffer has room AND */ | | | 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 | /* * Don't read more data if we have what we need. */ while (!bufPtr || /* We got no buffer! OR */ (!IsBufferFull(bufPtr) && /* Our buffer has room AND */ ((Tcl_Size) BytesLeft(bufPtr) < bytesToRead))) { /* Not enough bytes in it yet * to fill the dst */ int code; moreData: code = GetInput(chanPtr); bufPtr = statePtr->inQueueHead; |
︙ | ︙ | |||
10450 10451 10452 10453 10454 10455 10456 | assert(!GotFlag(statePtr, CHANNEL_EOF) || GotFlag(statePtr, CHANNEL_STICKY_EOF|CHANNEL_ENCODING_ERROR) || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); assert(!(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) == (CHANNEL_EOF|CHANNEL_BLOCKED))); UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); | | | 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 | assert(!GotFlag(statePtr, CHANNEL_EOF) || GotFlag(statePtr, CHANNEL_STICKY_EOF|CHANNEL_ENCODING_ERROR) || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); assert(!(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) == (CHANNEL_EOF|CHANNEL_BLOCKED))); UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); return (Tcl_Size)(p - dst); } /* *---------------------------------------------------------------------- * * CopyEventProc -- * |
︙ | ︙ |
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. */ |
︙ | ︙ | |||
100 101 102 103 104 105 106 | 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. NULL here * signals the channel is dead because the * interpreter/thread containing its Tcl | | < | < < < < < < < < < < < > > | > | < | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 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 | 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. NULL here * signals the channel is dead because the * interpreter/thread containing its Tcl * command is gone. */ #if TCL_THREADS Tcl_ThreadId thread; /* Thread the 'interp' belongs to. == Handler thread */ Tcl_ThreadId owner; /* Thread owning the structure. == Channel thread */ #endif 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. * * 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 | | < < < < < < < < < < < | 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | /* * 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 } | < < < < < < < < < < < < < < < < < < | 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 | 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; } | < < < < < < | 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 | #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; } | < < < < < < | 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 | } #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; | < < | 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 | 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/io.test.
︙ | ︙ | |||
2410 2411 2412 2413 2414 2415 2416 | test io-28.6 { close channel in write event handler Should not produce a segmentation fault in a Tcl built with --enable-symbols and -DPURIFY | | > > | > > > > > > > | > > > > > | > > | < | > > | | | | | | 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 | test io-28.6 { close channel in write event handler Should not produce a segmentation fault in a Tcl built with --enable-symbols and -DPURIFY } -body { variable done variable res # Not a complete / correct channel implementation. Just enough # to exercise the crash - closing from a write handler after 0 [list coroutine c1 apply [list {} { variable done set chan [chan create w {apply {{cmd chan args} { switch $cmd { blocking - finalize { } watch { lappend ::timers286 [after 0 chan postevent $chan write] } initialize { list initialize finalize watch read write configure blocking } default { error [list {unexpected command} $cmd] } } }}}] chan configure $chan -blocking 0 while 1 { chan event $chan writable [list [info coroutine]] yield close $chan set done 1 return } } [namespace current]]] vwait [namespace current]::done return success } -cleanup { foreach timer $::timers286 {after cancel $timer} } -result success test io-28.7 { close channel in read event handler Should not produce a segmentation fault in a Tcl built with --enable-symbols and -DPURIFY } -body { variable done variable res after 0 [list coroutine c1 apply [list {} { variable done # Not a complete / correct channel implementation. Just enough # to exercise the crash - closing from a read handler set chan [chan create r {apply {{cmd chan args} { switch $cmd { blocking - finalize { } watch { lappend ::timers287 [after 0 chan postevent $chan read] } initialize { list initialize finalize watch read write configure blocking } default { error [list {unexpected command} $cmd] } } }}}] chan configure $chan -blocking 0 while 1 { chan event $chan readable [list [info coroutine]] yield close $chan set done 1 return } } [namespace current]]] vwait [namespace current]::done return success } -cleanup { foreach timer $::timers287 {after cancel $timer} } -result success test io-29.1 {Tcl_WriteChars, channel not writable} { list [catch {puts stdin hello} msg] $msg } {1 {channel "stdin" wasn't opened for writing}} test io-29.2 {Tcl_WriteChars, empty string} { file delete $path(test1) set f [open $path(test1) w] |
︙ | ︙ | |||
6167 6168 6169 6170 6171 6172 6173 6174 | set x } -cleanup { close $f4 } -result {initial foo eof} close $f test io-44.6 {FileEventProc procedure: write-only non-blocking channel} -setup { | > > > | | > > > | < > > > | > > | > > > > > > > > > > > > > > > > | < | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 | set x } -cleanup { close $f4 } -result {initial foo eof} close $f # Bug https://core.tcl-lang.org/tcl/info/de232b49f26da1c1 with a corrected # refchan implementation. refchans should be responsible for their own # event generation and the one in the bug report was not doing so. test io-44.6 {FileEventProc procedure: write-only non-blocking channel} -setup { } -constraints {stdio fileevent} -body { namespace eval refchan { namespace ensemble create namespace export * # Change to taste depending on how much CPU you want to hog variable delay 0 proc finalize {chan args} { namespace upvar c_$chan timer timer catch {after cancel $timer} namespace delete c_$chan } proc initialize {chan args} { namespace eval c_$chan {} namespace upvar c_$chan watching watching timer timer set watching {} list finalize initialize seek watch write } proc watch {chan args} { namespace upvar c_$chan watching watching foreach arg $args { switch $arg { write { if {$arg ni $watching} { lappend watching $arg } } } } update $chan } proc write {chan args} { return 1 } # paraphrased from tcllib proc update {chan} { namespace upvar c_$chan watching watching timer timer variable delay catch {after cancel $timer} if {"write" in $watching} { set timer [after idle after $delay \ [namespace code [list post $chan]]] } } # paraphrased from tcllib proc post {chan} { variable delay namespace upvar c_$chan watching watching timer timer if {"write" in $watching} { set timer [after idle after $delay \ [namespace code [list post $chan]]] chan postevent $chan write } } } set f [chan create w [namespace which refchan]] chan configure $f -blocking 0 set data "some data" set x 0 chan event $f writable [namespace code { puts $f $data incr count [string length $data] if {$count > 262144} { chan event $f writable {} set x done } }] # Note: timeout needs to be very long under valgrind set token [after 240000 [namespace code { set x timeout }]] vwait [namespace which -variable x] return $x } -cleanup { after cancel $token catch {chan close $f} } -result done # Bug https://core.tcl-lang.org/tcl/info/67a5eabbd3d1 with a corrected # refchan implementation. refchans that are not reentrant should use # event loop to post events and the script in the bug report was not # doing so. test io-44.7 {refchan + coroutine yield error } -setup { set bghandler [interp bgerror {}] namespace eval schan { namespace ensemble create namespace export * proc open {} { set chan [chan create read [namespace current]] } proc initialize {chan mode} { return [list initialize finalize read watch] } proc finalize args {} proc read {chan count} {} proc watch {chan eventspec} { foreach event $eventspec { after idle after 0 chan postevent $chan $event } } } } -cleanup { interp bgerror {} $bghandler unset -nocomplain ::io-44.7-result namespace delete schan } -body { interp bgerror {} [list apply {{res opts} { set ::io-44.7-result [dict get $opts -errorinfo] }}] coroutine c1 apply [list {} { set chan [schan::open] chan event $chan readable [list [info coroutine]] yield close $chan set ::io-44.7-result success } [namespace current]] vwait ::io-44.7-result set ::io-44.7-result } -result success makeFile "foo bar" foo test io-45.1 {DeleteFileEvent, cleanup on close} {fileevent} { set f [open $path(foo) r] fileevent $f readable [namespace code { lappend x "binding triggered: \"[gets $f]\"" |
︙ | ︙ |
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 { |
︙ | ︙ |