Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix for de232b49f2, write-only nonblocking refchan and Tcl internal buffers. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | bug-de232b49f2 |
Files: | files | file ages | folders |
SHA3-256: |
d0dd6d19a41ae7afbc14dc9b38f447ff |
User & Date: | pooryorick 2019-04-27 07:19:49 |
References
2024-03-28
| ||
11:44 | • New ticket [080f846fd5] Channel system generating writable events BEFORE channel is open (refchan). artifact: f54756de6b user: apnadkarni | |
2019-04-27
| ||
07:24 | • Ticket [de232b49f2] write-only nonblocking refchan and Tcl internal buffers status still Open with 3 other changes artifact: 0657042b24 user: pooryorick | |
Context
2019-05-01
| ||
10:44 | Check for BG_FLUSH_SCHEDULED inside ChannelTimerProc Closed-Leaf check-in: 9d5631ebf8 user: pooryorick tags: bug-de232b49f2 | |
06:33 | merge bug-de232b49f2 check-in: bc1f88d020 user: pooryorick tags: trunk | |
2019-04-27
| ||
07:19 | Fix for de232b49f2, write-only nonblocking refchan and Tcl internal buffers. check-in: d0dd6d19a4 user: pooryorick tags: bug-de232b49f2 | |
2019-04-24
| ||
04:52 | merge bug-67a5eabbd3d1 check-in: 9bcec7cd88 user: pooryorick tags: core-8-branch | |
Changes
Changes to generic/tclIO.c.
︙ | ︙ | |||
3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 | 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; | > > > > > | 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 | TclDecrRefCount(statePtr->chanMsg); statePtr->chanMsg = NULL; } } Tcl_ClearChannelHandlers(chan); /* * Cancel any outstanding timer. */ Tcl_DeleteTimerHandler(statePtr->timer); /* * Invoke the registered close callbacks and delete their records. */ while (statePtr->closeCbPtr != NULL) { cbPtr = statePtr->closeCbPtr; statePtr->closeCbPtr = cbPtr->nextPtr; |
︙ | ︙ | |||
4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 | } if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) || (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) { if (FlushChannel(NULL, chanPtr, 0) != 0) { return -1; } } return total; } /* *--------------------------------------------------------------------------- * | > > | 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 | } if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) || (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) { if (FlushChannel(NULL, chanPtr, 0) != 0) { return -1; } } UpdateInterest(chanPtr); return total; } /* *--------------------------------------------------------------------------- * |
︙ | ︙ | |||
8471 8472 8473 8474 8475 8476 8477 | * - It does not process all events in the event queue, but only * one, at least in some situations. * * In that case we can get into a situation where * * - Tcl drops READABLE here, because it has data in its own * buffers waiting to be read by the extension. | | | | 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 | * - It does not process all events in the event queue, but only * one, at least in some situations. * * In that case we can get into a situation where * * - Tcl drops READABLE here, because it has data in its own * buffers waiting to be read by the extension. * - A READABLE event is synthesized via timer. * - The OS still reports the EXCEPTION condition on the file. * - And the extension gets the EXCEPTION event first, and handles * this as EOF. * * End result ==> Premature end of reading from a file. * * The concrete example is 'Expect', and its [expect] command * (and at the C-level, deep in the bowels of Expect, * 'exp_get_next_event'. See marker 'SunOS' for commentary in |
︙ | ︙ | |||
8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 | if (!statePtr->timer) { statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc, chanPtr); } } } ChanWatch(chanPtr, mask); } /* *---------------------------------------------------------------------- * * ChannelTimerProc -- | > > > > > > > > > > | 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 | if (!statePtr->timer) { statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc, chanPtr); } } } if (statePtr->timer == NULL && mask & TCL_WRITABLE && GotFlag(statePtr, CHANNEL_NONBLOCKING)) { statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, ChannelTimerProc,chanPtr); } ChanWatch(chanPtr, mask); } /* *---------------------------------------------------------------------- * * ChannelTimerProc -- |
︙ | ︙ | |||
8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 | static void ChannelTimerProc( ClientData clientData) { Channel *chanPtr = clientData; ChannelState *statePtr = chanPtr->state; /* State info for channel */ 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); | > > > > > > > > > > > > > < < < > | 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 | static void ChannelTimerProc( ClientData clientData) { Channel *chanPtr = clientData; ChannelState *statePtr = chanPtr->state; /* State info for channel */ Tcl_Preserve(statePtr); statePtr->timer = NULL; if (statePtr->interestMask & TCL_WRITABLE && GotFlag(statePtr, CHANNEL_NONBLOCKING)) { /* * 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_NotifyChannel((Tcl_Channel) chanPtr, TCL_WRITABLE); } 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_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE); } else { UpdateInterest(chanPtr); } Tcl_Release(statePtr); } /* *---------------------------------------------------------------------- * * Tcl_CreateChannelHandler -- * |
︙ | ︙ |
Changes to tests/io.test.
︙ | ︙ | |||
5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 | vwait [namespace which -variable x] vwait [namespace which -variable x] close $f4 set x } {initial foo eof} close $f 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]\"" fileevent $f readable {} | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 | vwait [namespace which -variable x] vwait [namespace which -variable x] close $f4 set x } {initial foo eof} close $f test chan-io-44.6 {FileEventProc procedure: write-only non-blocking channel} -setup { } -constraints {stdio unixExecs fileevent openpipe} -body { namespace eval refchan { namespace ensemble create namespace export * proc finalize {chan args} { namespace delete c_$chan } proc initialize {chan args} { namespace eval c_$chan {} namespace upvar c_$chan watching watching 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 } chan postevent $chan $arg } } } } proc write {chan args} { chan postevent $chan write return 1 } } 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 } }] after 10000 [namespace code { set x timeout }] vwait [namespace which -variable x] return $x } -cleanup { catch {chan close $f} } -result done 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]\"" fileevent $f readable {} |
︙ | ︙ |