about summary refs log tree commit diff
path: root/library/std/src/io
AgeCommit message (Collapse)AuthorLines
2020-11-13move copy specialization tests to their own moduleThe8472-181/+182
2020-11-13move copy specialization into sys::unix moduleThe8472-377/+8
2020-11-13add benchmarksThe8472-1/+131
2020-11-13reduce syscalls by inferring FD types based on source struct instead of ↵The8472-66/+102
calling stat() also adds handling for edge-cases involving large sparse files where sendfile could fail with EOVERFLOW
2020-11-13add forwarding specializations for &mut variantsThe8472-0/+21
`impl Write for &mut T where T: Write`, thus the same should apply to the specialization traits
2020-11-13prioritize sendfile over splice since it results in fewer context switches ↵The8472-16/+16
when sending to pipes splice returns to userspace when the pipe is full, sendfile just blocks until it's done, this can achieve much higher throughput
2020-11-13move tests module into separate fileThe8472-57/+52
2020-11-13hide unused exports on other platformsThe8472-3/+3
2020-11-13specialize io::copy to use copy_file_range, splice or sendfileThe8472-73/+469
Currently it only applies to linux systems. It can be extended to make use of similar syscalls on other unix systems.
2020-11-10Merge set_panic and set_print into set_output_capture.Mara Bos-90/+30
There were no use cases for setting them separately. Merging them simplifies some things.
2020-11-10Use Cell instead of RefCell for LOCAL_{STDOUT,STDERR}.Mara Bos-15/+16
2020-11-10Use Vec<u8> for LOCAL_STD{OUT,ERR} instead of dyn Write.Mara Bos-38/+23
It was only ever used with Vec<u8> anyway. This simplifies some things. - It no longer needs to be flushed, because that's a no-op anyway for a Vec<u8>. - Writing to a Vec<u8> never fails. - No #[cfg(test)] code is needed anymore to use `realstd` instead of `std`, because Vec comes from alloc, not std (like Write).
2020-11-10Remove io::LocalOutput and use Arc<Mutex<dyn>> for local streams.Mara Bos-52/+21
2020-11-08Rollup merge of #78811 - a1phyr:const_io_structs, r=dtolnayMara Bos-7/+27
Make some std::io functions `const` Tracking issue: #78812 Make the following functions `const`: - `io::Cursor::new` - `io::Cursor::get_ref` - `io::Cursor::position` - `io::empty` - `io::repeat` - `io::sink` r? `````@dtolnay`````
2020-11-06Add tracking issueBenoît du Garreau-6/+6
2020-11-06Make some std::io functions `const`Benoît du Garreau-7/+27
Includes: - io::Cursor::new - io::Cursor::get_ref - io::Cursor::position - io::empty - io::repeat - io::sink
2020-11-05document HACKsPeter Jaszkowiak-0/+2
2020-11-05Intra-doc links for std::io::bufferedPeter Jaszkowiak-3/+3
2020-10-27Auto merge of #78227 - SergioBenitez:test-stdout-threading, r=m-ou-sebors-9/+39
Capture output from threads spawned in tests This is revival of #75172. Original text: > Fixes #42474. > > r? `@​dtolnay` since you expressed interest in this, but feel free to redirect if you aren't the right person anymore. --- Closes #75172.
2020-10-26fix(docs): typo in BufWriter documentationMichele Lacchia-1/+1
2020-10-22Only load LOCAL_STREAMS if they are being usedSergio Benitez-0/+5
2020-10-22Capture output from threads spawned in testsTyler Mandry-9/+34
Fixes #42474.
2020-10-16Rollup merge of #76084 - Lucretiel:split-buffered, r=dtolnayDylan DPC-1438/+1463
Refactor io/buffered.rs into submodules This pull request splits `BufWriter`, `BufReader`, `LineWriter`, and `LineWriterShim` (along with their associated tests) into separate submodules. It contains no functional changes. This change is being made in anticipation of adding another type of buffered writer which can be switched between line- and block-buffering mode. Part of a series of pull requests resolving #60673.
2020-09-27Optimize set_{panic,print}(None).Mara Bos-0/+8
2020-09-27Relax memory ordering of LOCAL_STREAMS and document it.Mara Bos-5/+17
2020-09-27Only use LOCAL_{STDOUT,STDERR} when set_{print/panic} is used.Mara Bos-23/+40
The thread local LOCAL_STDOUT and LOCAL_STDERR are only used by the test crate to capture output from tests when running them in the same process in differen threads. However, every program will check these variables on every print, even outside of testing. This involves allocating a thread local key, and registering a thread local destructor. This can be somewhat expensive. This change keeps a global flag (LOCAL_STREAMS) which will be set to true when either of these local streams is used. (So, effectively only in test and benchmark runs.) When this flag is off, these thread locals are not even looked at and therefore will not be initialized on the first output on every thread, which also means no thread local destructors will be registered.
2020-09-27Auto merge of #77154 - fusion-engineering-forks:lazy-stdio, r=dtolnaybors-100/+40
Remove std::io::lazy::Lazy in favour of SyncOnceCell The (internal) std::io::lazy::Lazy was used to lazily initialize the stdout and stdin buffers (and mutexes). It uses atexit() to register a destructor to flush the streams on exit, and mark the streams as 'closed'. Using the stream afterwards would result in a panic. Stdout uses a LineWriter which contains a BufWriter that will flush the buffer on drop. This one is important to be executed during shutdown, to make sure no buffered output is lost. It also forbids access to stdout afterwards, since the buffer is already flushed and gone. Stdin uses a BufReader, which does not implement Drop. It simply forgets any previously read data that was not read from the buffer yet. This means that in the case of stdin, the atexit() function's only effect is making stdin inaccessible to the program, such that later accesses result in a panic. This is uncessary, as it'd have been safe to access stdin during shutdown of the program. --- This change removes the entire io::lazy module in favour of SyncOnceCell. SyncOnceCell's fast path is much faster (a single atomic operation) than locking a sys_common::Mutex on every access like Lazy did. However, SyncOnceCell does not use atexit() to drop the contained object during shutdown. As noted above, this is not a problem for stdin. It simply means stdin is now usable during shutdown. The atexit() call for stdout is moved to the stdio module. Unlike the now-removed Lazy struct, SyncOnceCell does not have a 'gone and unusable' state that panics. Instead of adding this again, this simply replaces the buffer with one with zero capacity. This effectively flushes the old buffer *and* makes any writes afterwards pass through directly without touching a buffer, making print!() available during shutdown without panicking. --- In addition, because the contents of the SyncOnceCell are no longer dropped, we can now use `&'static` instead of `Arc` in `Stdout` and `Stdin`. This also saves two levels of indirection in `stdin()` and `stdout()`, since Lazy effectively stored a `Box<Arc<T>>`, and SyncOnceCell stores the `T` directly.
2020-09-24Call ReentrantMutex::init() in stdout().Mara Bos-1/+3
2020-09-24Drop use of Arc from Stdin and Stdout.Mara Bos-27/+23
2020-09-24Remove std::io::lazy::Lazy in favour of SyncOnceCellMara Bos-98/+40
The (internal) std::io::lazy::Lazy was used to lazily initialize the stdout and stdin buffers (and mutexes). It uses atexit() to register a destructor to flush the streams on exit, and mark the streams as 'closed'. Using the stream afterwards would result in a panic. Stdout uses a LineWriter which contains a BufWriter that will flush the buffer on drop. This one is important to be executed during shutdown, to make sure no buffered output is lost. It also forbids access to stdout afterwards, since the buffer is already flushed and gone. Stdin uses a BufReader, which does not implement Drop. It simply forgets any previously read data that was not read from the buffer yet. This means that in the case of stdin, the atexit() function's only effect is making stdin inaccessible to the program, such that later accesses result in a panic. This is uncessary, as it'd have been safe to access stdin during shutdown of the program. --- This change removes the entire io::lazy module in favour of SyncOnceCell. SyncOnceCell's fast path is much faster (a single atomic operation) than locking a sys_common::Mutex on every access like Lazy did. However, SyncOnceCell does not use atexit() to drop the contained object during shutdown. As noted above, this is not a problem for stdin. It simply means stdin is now usable during shutdown. The atexit() call for stdout is moved to the stdio module. Unlike the now-removed Lazy struct, SyncOnceCell does not have a 'gone and unusable' state that panics. Instead of adding this again, this simply replaces the buffer with one with zero capacity. This effectively flushes the old buffer *and* makes any writes afterwards pass through directly without touching a buffer, making print!() available during shutdown without panicking.
2020-09-21Rollup merge of #76275 - FedericoPonzi:immutable-write-impl-73836, r=dtolnayecstatic-morse-0/+78
Implementation of Write for some immutable ref structs Fixes #73836
2020-09-21Updates stability attributes to the current nightly versionFederico Ponzi-3/+3
2020-09-11Deduplicates io::Write implementationsFederico Ponzi-14/+14
2020-09-10move buffered.rs to mod.rsNathan West-1/+1
2020-09-10Refactor io/buffered.rs into submodulesNathan West-1306/+1331
2020-09-07Auto merge of #74366 - t-rapp:tr-bufreader-pos, r=LukasKalbertodtbors-0/+87
Implement Seek::stream_position() for BufReader Optimization over `BufReader::seek()` for getting the current position without flushing the internal buffer. Related to #31100. Based on the code in #70577.
2020-09-07Implement Seek::stream_position() for BufReaderTobias Rapp-0/+87
Optimization over BufReader::seek() for getting the current position without flushing the internal buffer. Related to #31100. Based on code in #70577.
2020-09-03More implementations of Write for immutable refsFederico Ponzi-0/+78
Fixes #73836
2020-09-02Read: adjust a FIXME referenceRalf Jung-5/+8
2020-09-01Auto merge of #76047 - Dylan-DPC:rename/maybe, r=RalfJungbors-4/+4
rename get_{ref, mut} to assume_init_{ref,mut} in Maybeuninit References #63568 Rework with comments addressed from #66174 Have replaced most of the occurrences I've found, hopefully didn't miss out anything r? @RalfJung (thanks @danielhenrymantilla for the initial work on this)
2020-08-31std: move "mod tests/benches" to separate filesLzu Tao-2164/+2149
Also doing fmt inplace as requested.
2020-08-30update fixmesDPC-1/+1
2020-08-29rename get_{ref, mut} to assume_init_{ref,mut} in MaybeuninitDPC-3/+3
2020-08-28Auto merge of #72808 - Lucretiel:line-writer-reimpl, r=Amanieubors-176/+785
Substantial refactor to the design of LineWriter # Preamble This is the first in a series of pull requests designed to move forward with https://github.com/rust-lang/rust/issues/60673 (and the related [5 year old FIXME](https://github.com/rust-lang/rust/blob/ea7181b5f7a888c2cf969ae86de7207fa5fb40aa/src/libstd/io/stdio.rs#L459-L461)), which calls for an update to `Stdout` such that it can be block-buffered rather than line-buffered under certain circumstances (such as a `tty`, or a user setting the mode with a function call). This pull request refactors the logic `LineWriter` into a `LineWriterShim`, which operates on a `BufWriter` by mutable reference, such that it is easy to invoke the line-writing logic on an existing `BufWriter` without having to construct a new `LineWriter`. Additionally, fixes #72721 ## A note on flushing Because the word **flush** tends to be pretty overloaded in this discussion, I'm going to use the word **unbuffered** to refer to a `BufWriter` sending its data to the wrapped writer via `write`, without calling `flush` on it, and I'll be using **flushed** when referring to sending data via flush, which recursively writes the data all the way to the final sink. For example, given a `T = BufWriter<BufWriter<File>>`, saying that `T` **unbuffers** its data means that it is sent to the inner `BufWriter`, but not necessarily to the `File`, whereas saying that `T` **flushes** its data means that causes it (via `Write::flush`) to be delivered all the way to `File`. # Goals Once it became clear (for reasons described below) that the best way to approach this would involve refactoring `LineWriter` to work more directly on `BufWriter`'s internals, I established the following design goals for the refactor: - Do not duplicate logic with `BufWriter`. It's great at buffering and then unbuffering data, so use the existing logic as much as possible. - Minimize superfluous copying of data into `BufWriter`'s buffer. - Eliminate calls to `BufWriter::flush` and instead do the same thing as `BufWriter::write`, which is to only write to the wrapped writer (rather than flushing all the way down to the final data sink). - Uphold the "at-most 1 write of new data" convention of `Write::write` - Minimize or eliminate dropping errors (that is, eliminate the parts of the old design that threw away errors because `write` *must* report if any bytes were written) - As much as possible, attempt to fully flush completed lines, and *not* flush partial lines. One of the advantages of this design is that, so long as we don't encounter lines larger than the `BufWriter`'s capacity, partial lines will never be unbuffered, while completed lines will *always* be unbuffered (with subsequent calls to `LineWriter::write` retrying failed writes before processing new data. # Design There are two major & related parts of the design. First, a new internal stuct, `LineWriterShim`, is added. This struct implements all of the actual logic of line-writing in a `Write` implementation, but it only operates on an `&mut BufWriter`. This means that this shim can be constructed on-the-fly to apply line writing logic to an existing `BufWriter`. This is in fact how `LineWriter` has been updated to operate, and it is also how `Stdout` is being updated in my [development branch](https://github.com/Lucretiel/rust/tree/stdout-block-buffer) to switch which mode it wants to use at runtime. [An example of how this looks in practice](https://github.com/Lucretiel/rust/blob/f24f272df674dc7fa8941b97b45f41ad08b2199b/src/libstd/io/stdio.rs#L479-L484 ) The second major part of the design that the line-buffering logic, implemented in `LineWriterShim`, has been updated to work slightly more directly on the internals of `BufWriter`. Mostly it makes us of the public interface—particularly `buffer()` and `get_mut()`—but it also controls the flushing of the buffer with `flush_buf` rather than `flush`, and it writes to the buffer infallibly with a new `write_to_buffer` method. This has several advantages: - Data no longer has to round trip through the `BufWriter`'s buffer. If the user provides a complete line, that line is written directly to the inner writer (after ensuring the existing buffer is flushed). - The conventional contract of `write`—that at-most 1 attempt to write new data is made—is much more cleanly upheld, because we don't have to perform fallible flushes and perform semi-complicated logic of trying to pretend errors at different stages didn't happen. Instead, after attempting to write lines directly to the buffer, we can infallibly add trailing data to the buffer without allowing any attempts to continue writing it to the `inner` writer. - Perhaps most importantly, `LineWriter` *no longer performs a full flush on every line.* This makes its behavior much more consistent with `BufWriter`, which unbuffers data to its inner writer, without trying to flush it all the way to the final device. Previously, `LineWriter` had no choice but to use `flush` to ensure that the lines were unbuffered, but by writing directly to `inner` via `get_mut()` (when appropriate), we can use a more correct behavior. ## New(ish) line buffering logic The logic for line writing has been cleaned up, as described above. It now follows this algorithm for `write`, with minor adjustments for `write_all` and `write_vectored`: - Does our input data contain a newline? - If no: - simply use the regular `BufWriter::write` to write it; this will append it to the buffer and/or flush it as necessary based on how full the buffer is and how much input data there is. - additionally, if the current buffer ends with `'\n'`, attempt to immediately flush it with `flush_buf` before calling `BufWriter::write` This reproduces the old `needs_flush` behavior and ensures completed lines are flushed as soon as possible. The reason we only check if the buffer *ends* with `'\n'` is discussed later. - If yes: - First, `flush_buf` - Then use `bufwriter.get_mut().write()` to write the input data directly to the underlying writer, up to the last newline. Make at most one attempt at this. - If it errors, return the error - If it succeeds with a full write, add the remaining data (between the last newline and the end of the input) to the buffer. In order to uphold the "at-most 1 attempt to write new data" convention, no attempts are made to write this data to the inner writer (though obviously a subsequent write may immediately flush it, e.g., if it totally filled the buffer's capacity. - If it only partially succeeds, buffer the data only up to the last newline. We do this to try to avoid writing partial lines to the inner writer where possible (that is, whenever the lines are shorter than the total buffer capacity). While it was not my intention for this behavior to diverge from this existing `LineWriter` algorithm, this updated design emerged very naturally once `LineWriter` wasn't burdened with having to only operate via `BufWriter::flush`. There essentially two main changes to observable behavior: - `flush` is no longer used to unbuffer lines. The are only written to the writer wrapped by `LineWriter`; this inner writer might do its own buffering. This change makes `LineWriter` consistent with the behavior of `BufWriter`. This is probably the most obvious user-visible change; it's the one I most expect to provoke issue reports, if any are provoked. - Unless a line exceeds the capacity of the buffer, partial lines are not unbuffered (without the user manually calling flush). This is a less surprising behavior, and is enabled because `LineWriter` now has more precise control of what data is buffered and when it is unbuffered. I'd be surprised if anyone is relying on `LineWriter` unbuffering or flushing *partial* lines that are shorter than the capacity, so I'm not worried about this one. None of these changes are inconsistent with any published documentation of `LineWriter`. Nonetheless, like all changes with user-facing behavior changes, this design will obviously have to be very carefully scrutinized. # Alternative designs and design rationalle The initial goal of this project was to provide a way for the `LineWriter` logic to be operable directly on a `BufWriter`, so that the updated `Stdout` doesn't need to do something convoluted like `enum { BufWriter, LineWriter }` (which ends up being ~~impossible~~ difficult to transition between states after being constructed). The design went through several iterations before arriving at the current draft. The major first version simply involved adding methods like `write_line_buffered` to `BufWriter`; these would contain the actual logic of line-buffered writing, and would additionally have the advantages (described above) of operating directly on the internals of `BufWriter`. The idea was that `LineWriter` would simply call these methods, and the updated `Stdout` would use either `BufWriter::write` or `BufWriter::write_line_buffered`, depending on what mode it was in. The major issue with this design is that it loses the ability to take advantage of the `io::Write` trait, which provides several useful default implementations of the various io methods, such as `write_fmt` and `write_all`, just using the core methods. For this reason, the `write_line_buffered` design was retained, but moved into a separate struct called `LineWriterShim` which operates on an `&mut LineWriter`. As part of this move, the logic was lightly retooled to not touch the innards of `BufWriter` directly, but instead to make use of the unexported helper methods like `flush_buf`. The other design evolutions were mostly related to answering questions like "how much data should be buffered", "how should partial line writes be handled", etc. As much as possible I tried to answer these by emulating the current `LineWriter` logic (which, for example, retries partial line writes on subsequent calls to `write`) while still meeting the refactor design goals. # Next steps ~Currently, this design fails a few `LineWriter` tests, mostly because they expect `LineWriter` to *fully* flush its content. There are also some changes to the way that `LineWriter` buffers data *after* writing completed lines, aimed at ensuring that partial lines are not unbuffered prematurely. I want to make sure I fully understand the intent behind these tests before I either update the test or update this design so that they pass.~ However, in the meantime I wanted to get this published so that feedback could start to accumulate on it. There's a lot of errata around how I arrived at this design that didn't really fit in this overlong document, so please ask questions about anything that confusing or unclear and hopefully I can explain more of the rationale that led to it. # Test updates This design required some tests to be updated; I've research the intent behind these tests (mostly via `git blame`) and updated them appropriately. Those changes are cataloged here. - `test_line_buffer_fail_flush`: This test was added as a regression test for #32085, and is intended to assure that an errors from `flush` aren't propagated when preceded by a successful `write`. Because type of issue is no longer possible, because `write` calls `buffer.get_mut().write()` instead of `buffer.write(); buffer.flush();`, I'm simply removing this test entirely. Other, similar error invariants related to errors during write-retrying are handled in other test cases. - `erroneous_flush_retried`: This test was added as a regression test for #37807, and was intended to ensure that flush-retrying (via `needs_flush`) and error-ignoring were being handled correctly (ironically, this issue was caused by the flush-error-ignoring, above). Half of that issue is not possible by design with this refactor, because we no longer make fallible i/o calls that might produce errors we have to ignore after unbuffering lines. The `should_flush` behavior is captured by checking for a trailing newline in the `LineWriter` buffer; this test now checks that behavior. - `line_vectored`: changes here were pretty minor, mostly related to when partial lines are or aren't written. The old implementation of `write_vectored` used very complicated logic to precisely determine the location of the last newline and precisely write up to that point; this required doing several consecutive fallible writes, with all the complex error handling or ignoring issues that come with it. The updated design does at-most one write of a subset of total buffers (that is, it doesn't split in the middle of a buffer), even if that means writing partial lines. One of the major advantages of the new design is that the underlying vectored write operation on the device can be taken advantage of, even with small writes, so long as they include a newline; previously these were unconditionally buffered then written. - `line_vectored_partial_and_errors`: Pretty similiar to `line_vectored`, above; this test is for basic error recovery in `write_vectored` for vectored writes. As previously discussed, the mocked behavior being tested for (errors ignored under certain circumstances) no occurs, so I've simplified the test while doing my best to retain its spirit.
2020-08-27Once again, x.py tidyNathan West-5/+1
2020-08-27Typo fixesNathan West-5/+5
2020-08-27Improvements to `LineWriter::write_all`Nathan West-26/+79
`LineWriter::write_all` now only emits a single write when writing a newline when there's already buffered data.
2020-08-23Convert str -> prim@str in `std`Joshua Nelson-1/+1
2020-08-21Remove wrapper type handling absent raw standard streamsTomasz Miąsko-100/+33
Raw standard streams are always available. Remove unused wrapper type that was supposed to be responsible for handling their absence.
2020-08-21Make raw standard stream constructors constTomasz Miąsko-3/+6