From: Wayne C. <wc...@us...> - 2010-02-04 10:58:16
|
TIP #361: RELEASING CHANNEL BUFFERS ===================================== Version: $Revision: 1.1 $ Author: Wayne Cuddy <wcuddy_at_useunix.net> State: Draft Type: Project Tcl-Version: 8.7 Vote: Pending Created: Wednesday, 03 February 2010 URL: http://purl.org/tcl/tip/361.html WebEdit: http://purl.org/tcl/tip/edit/361 Post-History: ------------------------------------------------------------------------- ABSTRACT ========== Tcl should provide a mechanism by which a channel's output buffer can be released without requiring that Tcl flush any remaining data in the buffer to the operating system. This is of particular interest with output mechanisms which can block indefinitely causing the interpreter to consume unnecessary resources or prevent the interpreter from exiting. PROBLEM ========= When working with processes that handle multiplexing/non-blocking I/O it is not uncommon to write, or call *puts*, with more data than the operating system will accept. Thus Tcl begins to buffer this data using internal buffers at the application level and flushes the data in the background. Problems arise when the consumer of this data, be it the other end of a socket, pipe or FIFO, refuse to read data and do not close the channel. This has adverse effects on the *close* function and consequently the interpreter/process when it attempts to flush and close channels during finalization. CONSEQUENCES -------------- 1. When *close* is issued on a non-blocking channel Tcl removes the channel from pool of accessible channels from the current interpreter and attempts to flush any remaining data in the output buffers before closing the channel. Since the other end of the channel is not consuming data Tcl will never be able to flush it's internal buffers nor close the lower level channel driver. In a multiplexing server which opens new channels this will eventually result in resource starvation or denial of service. 2. When *exit* is called Tcl attempts to flush all output buffers, if any open channel blocks the interpreter will be unable exit and must be forcibly terminated by OS specific mechanisms. PROPOSE API ADDITIONS ======================= Provide a function and/or flags to existing functions which can be used to clear internal buffers without flushing. * *chan clear* /channelId/ * *close -noflush* /channelId/ COPYRIGHT =========== This document has been placed in the public domain. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows |
From: Alexandre F. <ale...@gm...> - 2010-02-05 00:04:28
|
On 2/4/10, Wayne Cuddy <wc...@us...> wrote: > > TIP #361: RELEASING CHANNEL BUFFERS Uh, is it just me, or is the problem very different in 8.6 HEAD ? (in another terminal: mkfifo /tmp/fifo;sleep 9999 < /tmp/fifo) set ff [open /tmp/fifo w] fconfigure $ff -blocking 0 puts -nonewline $ff [string repeat A 999999] flush $ff close $ff exit => exits immediately Comment out the 'close' above, and the [exit] blocks again.... Note that this *might* be the effect of one of the shortcuts done in the 'exit reform' [Bug 2001201]. If this is confirmed, then maybe the TIP is unneeded, since we've already broken compatibility, not for [exit] alone, but for explicit close of nonblocking channels. I am sorry to say that Wayne after pushing you to file a TIP... but for once Tcl seems to be doing the Right Thing :} -Alex |
From: Fredderic U. <mag...@gm...> - 2010-02-05 05:09:48
|
On 5 February 2010 11:04, Alexandre Ferrieux <ale...@gm...> wrote: > On 2/4/10, Wayne Cuddy <wc...@us...> wrote: >> TIP #361: RELEASING CHANNEL BUFFERS > > Uh, is it just me, or is the problem very different in 8.6 HEAD ? > (in another terminal: mkfifo /tmp/fifo;sleep 9999 < /tmp/fifo) > set ff [open /tmp/fifo w] > fconfigure $ff -blocking 0 > puts -nonewline $ff [string repeat A 999999] > flush $ff > close $ff > exit > => exits immediately > > Comment out the 'close' above, and the [exit] blocks again.... > Note that this *might* be the effect of one of the shortcuts done in > the 'exit reform' [Bug 2001201]. I'd be inclined to think that the TIP is the right thing, and this is a bug. If I explicitly say [flush], I kind of assume TCL would understand that I really really would like the data to be written to the underlyng connection, even if that means waiting a while before carrying through with a [close] or [exit]. In a proper production system, the application should have both internal and external limits set on how long it's allowed to just sit there, and this TIP would allow those internal limits to be enforced on any remaining outgoign data, while leaving the default action to Do The Right Thing. With respect to the example, TCP socket connections should eventualy time out, the consumer should time out (again, due to internal and external limits - most Unix's provide mechanisms to do that and they're almost mandatory on at least a few mainframe type systems I've seen) which in fact does happen in the example above, and if those timeouts could possibly be too long, steps should have been taken to enforce more appropriate limits. I'm more interested now, in how much effort needs to be applied to ensure TCL *won't* dump unwritten data in this case, and how obvious that is to someone who might think of doing what you've just done in that example; imagine a small application that builds one specific component of a webpage, in a string of such components, doesn't want to have a whole pile of extra work to do just to make sure its data gets written at the appropriate time - that is, in many cases, the calling applications job in this instance. ALL it wants to have to worry about, is preparing its chunk of output, writing it, cleaning up (updating databases, etc.), and finishing. My personal preference would be that [flush]d data WILL be written, even if it has to wait indefinitely (unless the other end of the socket gets closed first), sort of like setting a madatory low-water mark on the output buffer. [close] will do its best to close as much of the connection now as possible, and the rest as soon as possible (ie. after unwritten data has been sent), with [flush] being a means to indicate that we really do want this data written. [exit] and falling off the end of the script should be equivelant, and should try to finish cleanly (including [close]ing any open channels, with everything that goes along with [close]). And there should be some mechanism to prevent waiting if so desired (normally, I'd hazard to guess that if you've written something to a channel, regardless of what mode that channel's in, it's probably for a pretty good reason unless you explicitly say otherwise). In the case where there wasn't just a flush, but the channel has been closed, I'd be willing to accept TCL would try its best to write it, but just close it and give up when it hits the [exit], and perhaps also if it finds it's run out of files. (The latter should help resolve the issue mentioned with denial of services, if at all possible.) Other options could also be no-flush and/or max-wait options to any/all of [close] and [exit], and even the channel itself through [fconfigure]. But a quick loop over all open channels, using this TIP to explicitly close them without flush, just before an [exit], would do the trick. Fredderic |
From: Alexandre F. <ale...@gm...> - 2010-02-05 10:52:01
|
On 2/5/10, Alexandre Ferrieux <ale...@gm...> wrote: > On 2/4/10, Wayne Cuddy <wc...@us...> wrote: > > > > TIP #361: RELEASING CHANNEL BUFFERS > > > Uh, is it just me, or is the problem very different in 8.6 HEAD ? > > Comment out the 'close' above, and the [exit] blocks again.... > Note that this *might* be the effect of one of the shortcuts done in > the 'exit reform' [Bug 2001201]. Update: the exit reform is not guilty, since this bug is also present in 8.4 and 8.5 HEADs ! I managed to put my hands on a 8.3.5, which doesn't have the bug. And Wayne has a (presumably older) 8.4 without the bug. We should be able to isolate the commit rather easily now ;-) What happens is that TclFinalizeIOSubsystem looks at the channels' state flags, and doesn't attempt the flush when certain flags are set. Among these, CHANNEL_CLOSED. This is Too Bad because a just-[closed] channel in its background flush trip has this bit set, along with BG_FLUSH_SCHEDULED. It then looks like the fix consists of reallowing a flush when both flags are set. Not doing it immediately since I have to figure out how to make that flush foreground again. Andreas must have an opinion ;-) -Alex |
From: Alexandre F. <ale...@gm...> - 2010-02-05 14:37:47
|
On 2/5/10, Alexandre Ferrieux <ale...@gm...> wrote: > > What happens is that TclFinalizeIOSubsystem looks at the channels' > state flags, and doesn't attempt the flush when certain flags are set. > Among these, CHANNEL_CLOSED. This is Too Bad because a just-[closed] > channel in its background flush trip has this bit set, along with > BG_FLUSH_SCHEDULED. It then looks like the fix consists of reallowing > a flush when both flags are set. Not doing it immediately since I have > to figure out how to make that flush foreground again. Bug now recorded as 2946474, and patch attached. -Alex |
From: Andreas K. <and...@ac...> - 2010-02-05 16:16:27
|
Alexandre Ferrieux wrote: > On 2/5/10, Alexandre Ferrieux <ale...@gm...> wrote: >> On 2/4/10, Wayne Cuddy <wc...@us...> wrote: >> > >> > TIP #361: RELEASING CHANNEL BUFFERS >> >> >> Uh, is it just me, or is the problem very different in 8.6 HEAD ? >> >> Comment out the 'close' above, and the [exit] blocks again.... >> Note that this *might* be the effect of one of the shortcuts done in >> the 'exit reform' [Bug 2001201]. > > > Update: the exit reform is not guilty, since this bug is also present > in 8.4 and 8.5 HEADs ! > > I managed to put my hands on a 8.3.5, which doesn't have the bug. And > Wayne has a (presumably older) 8.4 without the bug. We should be able > to isolate the commit rather easily now ;-) > > What happens is that TclFinalizeIOSubsystem looks at the channels' > state flags, and doesn't attempt the flush when certain flags are set. > Among these, CHANNEL_CLOSED. This is Too Bad because a just-[closed] > channel in its background flush trip has this bit set, along with > BG_FLUSH_SCHEDULED. It then looks like the fix consists of reallowing > a flush when both flags are set. Not doing it immediately since I have > to figure out how to make that flush foreground again. > > Andreas must have an opinion ;-) Yes. This is a hairy monster, and I am glad that you (Alex) are willing to look into it. That said I recommend to bisect the thing to see where the change came in, and then look in the context for more information about the change. I.e. to find out if it was accidental, or intentional, and in the case of the latter, why. One thing I seem to remember regarding closing of a channel is that there are tricks around serial devices, possibly only on windows. And I wonder if changes made in and around that area caused anything. And conversely, we have to be careful now to not break them with the proposed edits. -- Sincerely, Andreas Kupries <an...@ac...> Developer @ <http://www.activestate.com/> |
From: Donal K. F. <don...@ma...> - 2010-02-05 16:46:54
Attachments:
donal_k_fellows.vcf
|
On 05/02/2010 16:12, Andreas Kupries wrote: > One thing I seem to remember regarding closing of a channel is that there are > tricks around serial devices, possibly only on windows. And I wonder if changes > made in and around that area caused anything. And conversely, we have to be > careful now to not break them with the proposed edits. I can remember removing explicit destruction of the pending serial buffers on exit from Tcl on Unix. It was losing error messages that had already been committed to stderr but not consumed by the other side of the terminal. (Evil bug that...) But I don't think that had anything to do with Tcl's flushing really. Donal. |
From: Alexandre F. <ale...@gm...> - 2010-02-07 23:03:58
|
> > > > What happens is that TclFinalizeIOSubsystem looks at the channels' > > state flags, and doesn't attempt the flush when certain flags are set. > > Among these, CHANNEL_CLOSED. This is Too Bad because a just-[closed] > > channel in its background flush trip has this bit set, along with > > BG_FLUSH_SCHEDULED. It then looks like the fix consists of reallowing > > a flush when both flags are set. Not doing it immediately since I have > > to figure out how to make that flush foreground again. > > > > Andreas must have an opinion ;-) > > Yes. > > This is a hairy monster, and I am glad that you (Alex) are willing to look > into it. That said I recommend to bisect the thing to see where the change > came in, and then look in the context for more information about the change. > I.e. to find out if it was accidental, or intentional, and in the case of > the latter, why. Bisection done, see report 2946474. The culprit is Don's 2008-12-01 fix of your backport of my fix to a more obscure bug... Don, I'd appreciate a comment there, since you mentioned "crashing the test suite" and I get no crash, just a (possibly unrelated) timeout on httpold-4-12. In all cases the patch I proposed in the report tries to respect whatever intent was behind this fix, by refining the test instead of reverting to the simpler, older one. Please review :) -Alex |
From: Alexandre F. <ale...@gm...> - 2010-02-10 08:02:47
|
On 2/4/10, Wayne Cuddy <wc...@us...> wrote: > > TIP #361: RELEASING CHANNEL BUFFERS > ===================================== Now that the related bug 2946474 is under control, maybe we can proceed with the main TIP's subject. So the only thing yet to be settled is the API style. Wayne suggests: > > * *chan clear* /channelId/ > * *close -noflush* /channelId/ > Initially I thought that an fconfigure option would be better, because we could set it in advance for many channels, simplifying the exit stage (esp. useful after an error). However, I have since realized that another use case could benefit from the imperative API above: "true short writes". Indeed, Tcl's nonblocking write is a high-level tool that lets the programmer "fire and forget" about what he's just written. In contrast, C-level nonblocking writes just report the number of bytes actually written, letting the caller resubmit the unwritten part later if he sees fit. Currently, Tcl always "sees fit", and does so in background with a hidden [fileevent writable]. This implies that it is currently impossible at script level to emulate the lower-level short write semantics. I do know of cases where it matters: I've been forced to do that in C :/ Now, the second of Wayne's proposals, slightly extended, does the job: set discarded [chan clear $ch] Indeed, knowing the number of discarded bytes is equivalent to getting back the number of bytes actually written from [puts] (which is not puts' job today). Of course, the actual name of the subcommand can be any one of: chan clear chan discard chan drop chan unflush Reactions ? -Alex |
From: Lars H. <Lar...@re...> - 2010-02-10 11:07:35
|
Alexandre Ferrieux skrev: > Indeed, Tcl's nonblocking write is a high-level tool that lets the > programmer "fire and forget" about what he's just written. In > contrast, C-level nonblocking writes just report the number of bytes > actually written, letting the caller resubmit the unwritten part later > if he sees fit. Currently, Tcl always "sees fit", and does so in > background with a hidden [fileevent writable]. > > This implies that it is currently impossible at script level to > emulate the lower-level short write semantics. I do know of cases > where it matters: I've been forced to do that in C :/ > > Now, the second of Wayne's proposals, slightly extended, does the job: > > set discarded [chan clear $ch] Would that make $discarded the number of bytes that were discarded, or the actual material (bytearray) that was discarded? Lars Hellström |
From: Alexandre F. <ale...@gm...> - 2010-02-10 14:02:08
|
On 2/10/10, Lars Hellström <Lar...@re...> wrote: > Alexandre Ferrieux skrev: > > > Indeed, Tcl's nonblocking write is a high-level tool that lets the > > programmer "fire and forget" about what he's just written. In > > contrast, C-level nonblocking writes just report the number of bytes > > actually written, letting the caller resubmit the unwritten part later > > if he sees fit. Currently, Tcl always "sees fit", and does so in > > background with a hidden [fileevent writable]. > > > > This implies that it is currently impossible at script level to > > emulate the lower-level short write semantics. I do know of cases > > where it matters: I've been forced to do that in C :/ > > > > Now, the second of Wayne's proposals, slightly extended, does the job: > > > > set discarded [chan clear $ch] > > > > Would that make $discarded the number of bytes that were discarded, or the > actual material (bytearray) that was discarded? Ah sorry, the number of bytes, both cheaper, safer, and sufficient for the purpose. -Alex |
From: <tc...@us...> - 2010-02-10 21:16:34
|
On Wed, Feb 10, 2010 at 09:02:40AM +0100, Alexandre Ferrieux wrote: > On 2/4/10, Wayne Cuddy <wc...@us...> wrote: > > > > TIP #361: RELEASING CHANNEL BUFFERS > > ===================================== > > Now that the related bug 2946474 is under control, maybe we can > proceed with the main TIP's subject. > > So the only thing yet to be settled is the API style. > Wayne suggests: > > > > > * *chan clear* /channelId/ > > * *close -noflush* /channelId/ > > > > Initially I thought that an fconfigure option would be better, because > we could set it in advance for many channels, simplifying the exit > stage (esp. useful after an error). > > However, I have since realized that another use case could benefit > from the imperative API above: "true short writes". > > Indeed, Tcl's nonblocking write is a high-level tool that lets the > programmer "fire and forget" about what he's just written. In > contrast, C-level nonblocking writes just report the number of bytes > actually written, letting the caller resubmit the unwritten part later > if he sees fit. Currently, Tcl always "sees fit", and does so in > background with a hidden [fileevent writable]. > > This implies that it is currently impossible at script level to > emulate the lower-level short write semantics. I do know of cases > where it matters: I've been forced to do that in C :/ > > Now, the second of Wayne's proposals, slightly extended, does the job: > > set discarded [chan clear $ch] So according to your interface above is the following true? fconfigure $ch -blocking 0 puts -nonewline $ch "some big huge data string....." set pending [chan pending output $ch] set discarded [chan clear $ch] Assuming that would only be true in the case that the background writing by the interpreter was unable to flush any bytes between the 2 [set] calls. > > Indeed, knowing the number of discarded bytes is equivalent to getting > back the number of bytes actually written from [puts] (which is not > puts' job today). Additionally to be able to emulate what one can achieve with C, only if one to if needed for some reason, this would not work as easily since the semantics of how [puts] works would need be changed. You would need to be able to instruct [puts] not to queue bytes that is cannot immediately flush. Otherwise I'd assume you end up with a race condition trying to determine how much data is pending and how much you need to [puts]? IE, trying to determine your next offset in your output buffer. Unless I'm missing something... which could very well be the case:) But this is outside the scope of this TIP since the ability to clear and close the channel could be achieved with this interface. Wayne |
From: Alexandre F. <ale...@gm...> - 2010-02-10 22:24:15
|
On 2/10/10, tc...@us... <tc...@us...> wrote: > > So according to your interface above is the following true? > > fconfigure $ch -blocking 0 > puts -nonewline $ch "some big huge data string....." > set pending [chan pending output $ch] > > set discarded [chan clear $ch] I assume a line is missing here, and you're asking whether $pending==$discarded ? As a matter of fact, yes. I admit I overlooked [chan pending], so forget about my proposal of returning the same value. Thanks for the simplification :} > Assuming that would only be true in the case that the background writing > by the interpreter was unable to flush any bytes between the 2 [set] > calls. There's no separate thread doing "background" writing, it's merely an internal [fileevent writable]. So as long as we're not calling [update] nor [vwait] between the two lines, we're safe. > Additionally to be able to emulate what one can achieve with C, only if > one to if needed for some reason, this would not work as easily since > the semantics of how [puts] works would need be changed. You would need > to be able to instruct [puts] not to queue bytes that is cannot > immediately flush. Otherwise I'd assume you end up with a race condition > trying to determine how much data is pending and how much you need to > [puts]? IE, trying to determine your next offset in your output buffer. > Unless I'm missing something... which could very well be the case:) Nope, see above, no race condition, all this is within the interp's single thread. > But this is outside the scope of this TIP since the ability to clear and > close the channel could be achieved with this interface. Bottom line: I think your [chan clear] proposal, whatever the return value, covers all cases, as does the [fconfigure] approach. So now I'm listening to opinions about this "imperative vs fconfigure" question. I'm ready to implement either. -Alex |
From: Alexandre F. <ale...@gm...> - 2010-02-10 22:32:36
|
On 2/10/10, Alexandre Ferrieux <ale...@gm...> wrote: > > Bottom line: I think your [chan clear] proposal, whatever the return > value, covers all cases, as does the [fconfigure] approach. So now I'm > listening to opinions about this "imperative vs fconfigure" question. > I'm ready to implement either. Clarification: of course the [fconfigure] still doesn't allow for discarding in mid-life, so the question is more precisely, which one is preferred among: (a) Simplicity of [exit] with an [fconfigure -fastclose 1] at the beginning, no way to express short writes. (b) Expressivity of short writes with [puts;chan clear] at the cost of a slightly more complex loop over existing channels for a timely exit. -Alex |
From: <tc...@us...> - 2010-02-10 22:47:53
|
On Wed, Feb 10, 2010 at 11:32:29PM +0100, Alexandre Ferrieux wrote: > On 2/10/10, Alexandre Ferrieux <ale...@gm...> wrote: > > > > Bottom line: I think your [chan clear] proposal, whatever the return > > value, covers all cases, as does the [fconfigure] approach. So now I'm > > listening to opinions about this "imperative vs fconfigure" question. > > I'm ready to implement either. > > Clarification: of course the [fconfigure] still doesn't allow for > discarding in mid-life, so the question is more precisely, which one > is preferred among: > > (a) Simplicity of [exit] with an [fconfigure -fastclose 1] at the > beginning, no way to express short writes. > > (b) Expressivity of short writes with [puts;chan clear] at the cost > of a slightly more complex loop over existing channels for a timely > exit. > > -Alex I prefer (b) since it allows you to work with channels that may come back to life without necessitating a close-open sequence. Yes, it is a little more complex but that tends to be the case with additional flexibility. Ex. - the other side stops reading for an extended period of time - you clear the channel buffer and setup a writable handler - when your handler is called you can reset the other devices state with a 'sync' character(s) and continue with the conversation - yes, it's not common but I have some serial devices that end pausing/freezing occasionally then spewing and/or consuming garbage but we can "reset" the conversation with a sync char LOL, that's right I get to work with lots O buggy devices/software.. yay! The simpler case is it stops reading, I clear the channel buffer and send a sync char without needing to wait for my writable handler to be called. But I'll bet someone could persuade me choose (a) with good justification. Wayne |
From: Joe E. <jen...@fl...> - 2010-02-10 22:45:20
|
Alexandre Ferrieux wrote: > > Bottom line: I think your [chan clear] proposal, whatever the return > value, covers all cases, as does the [fconfigure] approach. So now I'm > listening to opinions about this "imperative vs fconfigure" question. > I'm ready to implement either. I'm not entirely sure what "the [fconfigure] approach" you mention is (did I miss an email somewhere?), but I will probably express a strong preference for [chan clear]. Rationale: [chan clear] means "discard any unwritten data, right now", which is specific and predictable. If the proposed (?) [fconfigure $chan -<mumble> true] means "this channel (is allowed to / is instructed to) discard pending data at some (specific? arbitrary?) point in the future", that's much less predictable; I don't want those semantics. OTOH if it's intended to mean "discard any unwritten data, right now" -- well, that's a verb, not an adjective, and should be spelled as one. --Joe English jen...@fl... |
From: Alexandre F. <ale...@gm...> - 2010-02-10 23:01:41
|
On 2/10/10, Joe English <jen...@fl...> wrote: > > Alexandre Ferrieux wrote: > > > > Bottom line: I think your [chan clear] proposal, whatever the return > > value, covers all cases, as does the [fconfigure] approach. So now I'm > > listening to opinions about this "imperative vs fconfigure" question. > > I'm ready to implement either. > > > > I'm not entirely sure what "the [fconfigure] approach" > you mention is (did I miss an email somewhere?), but I > will probably express a strong preference for [chan clear]. > > Rationale: [chan clear] means "discard any unwritten data, > right now", which is specific and predictable. > > If the proposed (?) [fconfigure $chan -<mumble> true] means > "this channel (is allowed to / is instructed to) discard pending data > at some (specific? arbitrary?) point in the future", that's > much less predictable; I don't want those semantics. > > OTOH if it's intended to mean "discard any unwritten data, right now" -- > well, that's a verb, not an adjective, and should be spelled > as one. The semantics I had in mind was "this channel will discard its output buffer on close". That's fully predictable, just less powerful since it doesn't solve the short read case. -Alex |