A while ago I stumbled across a technique for improving stream buffering that I wish more I/O library implementors knew about. Korn and Vo's sfio library (circa 1991) had a feature called "pooling", whereby distinct streams could be linked together. Any read or write operation to any stream in a pool implicitly synchronized all the other streams in the pool first. This way, when stdio and stderr were pooled, which was the default when both went to ttys, a write on stderr implicitly flushed stdout. I've implemented this feature for myself a couple times; it's fairly easy to do and basically eliminates the need to explicitly flush streams in client code.
Citation: https://archive.org/details/1991-proceedings-tech-conference... but note that the explanation of stream pools there is a little less precise and more general than really necessary. I believe that later versions of sfio simplified things somewhat, though I could be wrong. (I find their code fairly hard to read.)
Anyhow, ISTM a missed opportunity when new languages that don't actually use libc's routines for something reinvent POSIX's clunkier aspects.
> Surprisingly, Rust, as of now, uses line buffering for both TTYs and non-TTYs.
> The FIXME comment shows the Rust team acknowledges that ideally they should check if something is executed in TTYs or not and use LineWriter or BufWriter accordingly, but I guess this was not on their priority list.
This does not inspire confidence.
That's not forced behaviour. If you want to do something more interesting, you'd use the raw/unsynchronised handles:
/// The returned handle has no external synchronization or buffering layered on top.
const fn stdout_raw() -> StdoutRaw;
In libc, you can use setvbuf to change the buffering mode.
How would a modern OS implement this?
> How would a modern OS implement this?
fwrite only buffers because write is slow.
make it so write isn't slow and you don't need userspace buffering!
You do not need any OS changes, you just need a print library that does buffering correctly.
Buffering should basically always be: “Work or Time” based, either you buffered enough or enough time has passed. This is because you buffer when per-element latency starts bottlenecking your throughput.
If you have so little data that your throughput is not getting limited, then you should be flushing.
Probably by not assuming terminals and byte streams any more. Terminal-by-default is a 20th-century-ism. Now you have screens with pixels. Without stdout, no need to know if stdout is a terminal.
This is an interesting idea--that in a reimagined OS, programs could have their output connected to all sorts of sinks (terminal, file, GUI, web content) without carrying baggage related to those sinks' behaviors.
I think the core question is whether some middle layer of output processing between program and sink/display could be created that knows enough about (using terminals as an example sink) raw mode/console dimensions/buffering to make most programs display correctly enough for most users without knowing specifics about the program writing the output's internals. If that can be done, then programs that need more specifics (e.g. complex animated/ncurses GUIs) could either propose overrides/settings to the output middleware or configure it directly, and programs that don't wouldn't.
That's possible to implement, sure, but can that be done without just reinventing the POSIX terminal API, or any one of the bad multiplatform-simple-GUI APIs, badly?
> programs could have their output connected to all sorts of sinks (terminal, file, GUI, web content) without carrying baggage related to those sinks' behaviors.
We already have this. The TTY itself is not very special at all. It's just that the applications, traditionally, decide that they should special-case the writing to TTYs (because those, presumably, are human-oriented and should have as little batching as possible). But you, as an application developer, can simply not do this, you know.
A while ago I stumbled across a technique for improving stream buffering that I wish more I/O library implementors knew about. Korn and Vo's sfio library (circa 1991) had a feature called "pooling", whereby distinct streams could be linked together. Any read or write operation to any stream in a pool implicitly synchronized all the other streams in the pool first. This way, when stdio and stderr were pooled, which was the default when both went to ttys, a write on stderr implicitly flushed stdout. I've implemented this feature for myself a couple times; it's fairly easy to do and basically eliminates the need to explicitly flush streams in client code.
Citation: https://archive.org/details/1991-proceedings-tech-conference... but note that the explanation of stream pools there is a little less precise and more general than really necessary. I believe that later versions of sfio simplified things somewhat, though I could be wrong. (I find their code fairly hard to read.)
Anyhow, ISTM a missed opportunity when new languages that don't actually use libc's routines for something reinvent POSIX's clunkier aspects.
> Surprisingly, Rust, as of now, uses line buffering for both TTYs and non-TTYs.
> The FIXME comment shows the Rust team acknowledges that ideally they should check if something is executed in TTYs or not and use LineWriter or BufWriter accordingly, but I guess this was not on their priority list.
This does not inspire confidence.
That's not forced behaviour. If you want to do something more interesting, you'd use the raw/unsynchronised handles:
In libc, you can use setvbuf to change the buffering mode.
How would a modern OS implement this?
> How would a modern OS implement this?
fwrite only buffers because write is slow.
make it so write isn't slow and you don't need userspace buffering!
You do not need any OS changes, you just need a print library that does buffering correctly.
Buffering should basically always be: “Work or Time” based, either you buffered enough or enough time has passed. This is because you buffer when per-element latency starts bottlenecking your throughput.
If you have so little data that your throughput is not getting limited, then you should be flushing.
Probably by not assuming terminals and byte streams any more. Terminal-by-default is a 20th-century-ism. Now you have screens with pixels. Without stdout, no need to know if stdout is a terminal.
This is an interesting idea--that in a reimagined OS, programs could have their output connected to all sorts of sinks (terminal, file, GUI, web content) without carrying baggage related to those sinks' behaviors.
I think the core question is whether some middle layer of output processing between program and sink/display could be created that knows enough about (using terminals as an example sink) raw mode/console dimensions/buffering to make most programs display correctly enough for most users without knowing specifics about the program writing the output's internals. If that can be done, then programs that need more specifics (e.g. complex animated/ncurses GUIs) could either propose overrides/settings to the output middleware or configure it directly, and programs that don't wouldn't.
That's possible to implement, sure, but can that be done without just reinventing the POSIX terminal API, or any one of the bad multiplatform-simple-GUI APIs, badly?
> programs could have their output connected to all sorts of sinks (terminal, file, GUI, web content) without carrying baggage related to those sinks' behaviors.
We already have this. The TTY itself is not very special at all. It's just that the applications, traditionally, decide that they should special-case the writing to TTYs (because those, presumably, are human-oriented and should have as little batching as possible). But you, as an application developer, can simply not do this, you know.
[dead]