Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Substantial refactor to the design of LineWriter #72808

Merged
merged 4 commits into from
Aug 29, 2020

Conversation

Lucretiel
Copy link
Contributor

@Lucretiel Lucretiel commented May 31, 2020

Preamble

This is the first in a series of pull requests designed to move forward with #60673 (and the related 5 year old FIXME), 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 to switch which mode it wants to use at runtime.

An example of how this looks in practice

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 Don't return an error after a partial write in LineWriter #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 BrokenPipe sometimes not reported #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.

@rust-highfive
Copy link
Collaborator

r? @hanna-kruppe

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 31, 2020
@hanna-kruppe
Copy link
Contributor

Way outside my wheelhouse, and I currently have little time for reviews anyway. r? @BurntSushi

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-8 of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux x86_64-gnu-llvm-8
##[section]Starting: Initialize job
Agent name: 'Hosted Agent'
Agent machine name: 'fv-az619'
Current agent version: '2.169.1'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https://github.com/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.2)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/d8d954eb-84b8-4407-92c3-763f2a3401a1.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https://github.com/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/72808/merge:refs/remotes/pull/72808/merge
---
 ---> cb2676f08729
Step 5/8 : ENV RUST_CONFIGURE_ARGS       --build=x86_64-unknown-linux-gnu       --llvm-root=/usr/lib/llvm-8       --enable-llvm-link-shared       --set rust.thin-lto-import-instr-limit=10
 ---> Using cache
 ---> df25ce111862
Step 6/8 : ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy &&            python2.7 ../x.py test src/test/mir-opt --pass=build                                   --target=armv5te-unknown-linux-gnueabi &&            python2.7 ../x.py test src/tools/tidy
 ---> 599b9ac96b27
Step 7/8 : ENV NO_DEBUG_ASSERTIONS=1
 ---> Using cache
 ---> 091087e35a36
---
   Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
   Compiling chalk-rust-ir v0.10.0
   Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
   Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
   Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
   Compiling chalk-solve v0.10.0
   Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
   Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
   Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
   Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
   Compiling chalk-rust-ir v0.10.0
   Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
   Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
   Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
   Compiling chalk-solve v0.10.0
   Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
   Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
   Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
.................................................................................................... 1700/10276
..................................................................................i................. 1800/10276
.................................................................................................... 1900/10276
.................................................................................................... 2000/10276
...i..i............................................................................................. 2100/10276
.............................................................................................iiiii.. 2200/10276
.................................................................................................... 2400/10276
.................................................................................................... 2500/10276
.................................................................................................... 2600/10276
.................................................................................................... 2700/10276
---
........................i...............i........................................................... 5200/10276
.................................................................................................... 5300/10276
........................................................................i........................... 5400/10276
..................................................................i................................. 5500/10276
..................................................................................ii.ii........i...i 5600/10276
.........................i.......................................................................... 5800/10276
.................................i.................................................................. 5900/10276
.......................................................................................ii........... 6000/10276
..........................i......................................................................... 6100/10276
..........................i......................................................................... 6100/10276
.................................................................................................... 6200/10276
.................................................................................................... 6300/10276
.................................................ii...i..ii...........i............................. 6400/10276
.................................................................................................... 6600/10276
.................................................................................................... 6700/10276
.................................................................................................... 6700/10276
..................................................................................i..ii............. 6800/10276
.................................................................................................... 7000/10276
.................................................................................................... 7100/10276
....................................i............................................................... 7200/10276
.................................................................................................... 7300/10276
---
.................................................................................................... 8200/10276
.................................................................................................... 8300/10276
.........................................................................i.......................... 8400/10276
.................................................................................................... 8500/10276
...........................iiiiii.iiiiii.i.......................................................... 8600/10276
.................................................................................................... 8800/10276
.................................................................................................... 8900/10276
.................................................................................................... 9000/10276
.................................................................................................... 9100/10276
---
Suite("src/test/codegen") not skipped for "bootstrap::test::Codegen" -- not in ["src/tools/tidy"]
Check compiletest suite=codegen mode=codegen (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 192 tests
iiii......i..............ii.i..........i......................i...........i..i................i....i 100/192
.............i.i.i...iii..iiiiiiiiiiiiiiii.......................iii................ii......

 finished in 5.439
Suite("src/test/codegen-units") not skipped for "bootstrap::test::CodegenUnits" -- not in ["src/tools/tidy"]
Check compiletest suite=codegen-units mode=codegen-units (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
Suite("src/test/assembly") not skipped for "bootstrap::test::Assembly" -- not in ["src/tools/tidy"]
Check compiletest suite=assembly mode=assembly (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 21 tests
iiiiiiiiiiiiiiiiiiiii

 finished in 0.154
Suite("src/test/incremental") not skipped for "bootstrap::test::Incremental" -- not in ["src/tools/tidy"]
Check compiletest suite=incremental mode=incremental (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
Suite("src/test/debuginfo") not skipped for "bootstrap::test::Debuginfo" -- not in ["src/tools/tidy"]
Check compiletest suite=debuginfo mode=debuginfo (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 115 tests
iiiii..i.....i..i...i..i.i.i..i..i..ii....i.i....ii..........iiii.........i.....i..i.......ii.i.ii.. 100/115
...iiii.....ii.

 finished in 13.990
Suite("src/test/ui-fulldeps") not skipped for "bootstrap::test::UiFullDeps" -- not in ["src/tools/tidy"]
Uplifting stage1 rustc (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---

   Doc-tests core

running 2577 tests
......iiiii......................................................................................... 100/2577
.................................................................................................ii. 200/2577
......................................i............................................................. 400/2577
..............................................................................................i..i.. 500/2577
..............................................................................................i..i.. 500/2577
................iiii................................................................................ 600/2577
.................................................................................................... 800/2577
.................................................................................................... 900/2577
.................................................................................................... 1000/2577
.................................................................................................... 1100/2577
---

running 769 tests
.................................................................................................... 100/769
.................................................................................................... 200/769
.................................................FFFthread '<unnamed>' panicked at 'explicit panic', src/libstd/io/buffered.rs:1705:17
.thread '<unnamed>' panicked at 'explicit panic', src/libstd/io/stdio.rs:944:13
................................................................................................... 400/769
.................................................................................................... 500/769
...........................thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: RecvError', src/libstd/sync/mpsc/mod.rs:2741:22
---
 right: `2`', src/libstd/io/buffered.rs:1773:9

---- io::buffered::tests::line_vectored_partial_and_errors stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `[[97, 98, 99, 120, 10]]`,
 right: `[[97, 98, 99]]`', src/libstd/io/buffered.rs:1833:25
---- io::buffered::tests::test_line_buffer_fail_flush stdout ----
---- io::buffered::tests::test_line_buffer_fail_flush stdout ----
thread '<unnamed>' panicked at 'didn't flush on new line', src/libstd/io/buffered.rs:1605:17

failures:
    io::buffered::tests::erroneous_flush_retried
    io::buffered::tests::line_vectored
    io::buffered::tests::line_vectored
    io::buffered::tests::line_vectored_partial_and_errors
    io::buffered::tests::test_line_buffer_fail_flush

test result: FAILED. 765 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '-p std --lib'

command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "test" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-j" "2" "--release" "--locked" "--color" "always" "--features" "panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/checkout/src/libtest/Cargo.toml" "-p" "std" "--" "--quiet"
expected success, got: exit code: 101

---
  local time: Sun May 31 08:48:24 UTC 2020
  network time: Sun, 31 May 2020 08:48:25 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/72808/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (4343) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux mingw-check
##[section]Starting: Initialize job
Agent name: 'Azure Pipelines 2'
Agent machine name: 'fv-az578'
Current agent version: '2.169.1'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https://github.com/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.2)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/b2ad05e5-326c-49a0-b41d-3280ad6d5a42.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https://github.com/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/72808/merge:refs/remotes/pull/72808/merge
---
 ---> 3adb0605cc65
Step 6/7 : ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
 ---> Using cache
 ---> 28dbc326cb7f
Step 7/7 : ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors &&            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu &&            python3 ../x.py build --stage 0 src/tools/build-manifest &&            python3 ../x.py test --stage 0 src/tools/compiletest &&            python3 ../x.py test src/tools/tidy &&            python3 ../x.py doc --stage 0 src/libstd &&            /scripts/validate-toolstate.sh
 ---> 537a01811900
Successfully built 537a01811900
Successfully tagged rust-ci:latest
Built container sha256:537a018119009dc218456238dec90b5530050db1e2a1e166550c218003f6159d
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-rust-ir v0.10.0
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
configure: build.cargo-native-static := True
configure: llvm.assertions      := True
configure: rust.debug-assertions := True
configure: rust.channel         := nightly
configure: build.configure-args := ['--enable-sccache', '--disable-manage-submodu ...
configure: writing `config.toml` in current directory
configure: 
configure: run `python /checkout/x.py --help`
configure: 
---
Hugepagesize:       2048 kB
DirectMap4k:      139200 kB
DirectMap2M:     4055040 kB
DirectMap1G:     5242880 kB
+ python3 ../x.py test src/tools/expand-yaml-anchors
Ensuring the YAML anchors in the GitHub Actions config were expanded
Ensuring the YAML anchors in the GitHub Actions config were expanded
Building stage0 tool expand-yaml-anchors (x86_64-unknown-linux-gnu)
   Compiling unicode-xid v0.2.0
   Compiling syn v1.0.11
   Compiling linked-hash-map v0.5.2
   Compiling lazy_static v1.4.0
   Compiling lazy_static v1.4.0
   Compiling yaml-rust v0.4.3
   Compiling quote v1.0.2
   Compiling thiserror-impl v1.0.5
   Compiling thiserror v1.0.5
   Compiling yaml-merge-keys v0.4.0
   Compiling expand-yaml-anchors v0.1.0 (/checkout/src/tools/expand-yaml-anchors)
Build completed successfully in 0:00:24
+ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu
    Finished dev [unoptimized] target(s) in 0.17s
Checking rustdoc artifacts (x86_64-unknown-linux-gnu -> i686-pc-windows-gnu)
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-rust-ir v0.10.0
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
skip untracked path src/doc/rust-by-example/ during rustfmt invocations
skip untracked path src/llvm-project/ during rustfmt invocations
Diff in /checkout/src/libstd/io/buffered.rs at line 1321:
 
     impl Read for ShortReader {
         fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
-            if self.lengths.is_empty() {
-                Ok(0)
-                Ok(self.lengths.remove(0))
-            }
-            }
+            if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) }
     }
 
 
Running `"/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/rustfmt" "--config-path" "/checkout" "--edition" "2018" "--unstable-features" "--skip-children" "--check" "/checkout/src/libstd/io/buffered.rs"` failed.
If you're running `tidy`, try again with `--bless`. Or, if you just want to format code, run `./x.py fmt` instead.
Build completed unsuccessfully in 0:00:39
== clock drift check ==
  local time: Mon Jun  1 05:24:09 UTC 2020
  network time: Mon, 01 Jun 2020 05:24:09 GMT
  network time: Mon, 01 Jun 2020 05:24:09 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/72808/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (3502) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux mingw-check
##[section]Starting: Initialize job
Agent name: 'Azure Pipelines 2'
Agent machine name: 'fv-az578'
Current agent version: '2.169.1'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https://github.com/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.3)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/f45bdfcd-37a9-4854-8f4f-50c03819c8d2.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https://github.com/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/72808/merge:refs/remotes/pull/72808/merge
---
 ---> 3adb0605cc65
Step 6/7 : ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
 ---> Using cache
 ---> 28dbc326cb7f
Step 7/7 : ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors &&            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu &&            python3 ../x.py build --stage 0 src/tools/build-manifest &&            python3 ../x.py test --stage 0 src/tools/compiletest &&            python3 ../x.py test src/tools/tidy &&            python3 ../x.py doc --stage 0 src/libstd &&            /scripts/validate-toolstate.sh
 ---> 537a01811900
Successfully built 537a01811900
Successfully tagged rust-ci:latest
Built container sha256:537a018119009dc218456238dec90b5530050db1e2a1e166550c218003f6159d
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-rust-ir v0.10.0
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
configure: rust.debug-assertions := True
configure: rust.channel         := nightly
configure: build.cargo-native-static := True
configure: build.submodules     := False
configure: build.configure-args := ['--enable-sccache', '--disable-manage-submodu ...
configure: writing `config.toml` in current directory
configure: 
configure: run `python /checkout/x.py --help`
configure: 
---
Hugepagesize:       2048 kB
DirectMap4k:      147392 kB
DirectMap2M:     4046848 kB
DirectMap1G:     5242880 kB
+ python3 ../x.py test src/tools/expand-yaml-anchors
Ensuring the YAML anchors in the GitHub Actions config were expanded
Ensuring the YAML anchors in the GitHub Actions config were expanded
Building stage0 tool expand-yaml-anchors (x86_64-unknown-linux-gnu)
   Compiling unicode-xid v0.2.0
   Compiling syn v1.0.11
   Compiling linked-hash-map v0.5.2
   Compiling lazy_static v1.4.0
   Compiling lazy_static v1.4.0
   Compiling yaml-rust v0.4.3
   Compiling quote v1.0.2
   Compiling thiserror-impl v1.0.5
   Compiling thiserror v1.0.5
   Compiling yaml-merge-keys v0.4.0
   Compiling expand-yaml-anchors v0.1.0 (/checkout/src/tools/expand-yaml-anchors)
Build completed successfully in 0:00:27
+ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu
    Finished dev [unoptimized] target(s) in 0.19s
Checking rustdoc artifacts (x86_64-unknown-linux-gnu -> i686-pc-windows-gnu)
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-rust-ir v0.10.0
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
skip untracked path src/doc/rust-by-example/ during rustfmt invocations
skip untracked path src/llvm-project/ during rustfmt invocations
Diff in /checkout/src/libstd/io/buffered.rs at line 1357:
 
     impl Read for ShortReader {
         fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
-            if self.lengths.is_empty() {
-                Ok(0)
-                Ok(self.lengths.remove(0))
-            }
-            }
+            if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) }
     }
 
Diff in /checkout/src/libstd/io/buffered.rs at line 1789:
 
 
             match self.max_writes {
                 Some(0) if self.error_after_max_writes => {
-                    return Err(io::Error::new(io::ErrorKind::Other, "test - max_writes"))
+                    return Err(io::Error::new(io::ErrorKind::Other, "test - max_writes"));
                 Some(0) => return Ok(0),
                 Some(0) => return Ok(0),
                 Some(ref mut count) => *count -= 1,
Running `"/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/rustfmt" "--config-path" "/checkout" "--edition" "2018" "--unstable-features" "--skip-children" "--check" "/checkout/src/libstd/io/buffered.rs"` failed.
If you're running `tidy`, try again with `--bless`. Or, if you just want to format code, run `./x.py fmt` instead.
Build completed unsuccessfully in 0:00:36
== clock drift check ==
  local time: Mon Jun  1 21:57:31 UTC 2020
  network time: Mon, 01 Jun 2020 21:57:31 GMT
  network time: Mon, 01 Jun 2020 21:57:31 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/72808/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (4118) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, amazing work! I really appreciate the detailed write up. I found myself agreeing with everything you were saying and like the rationale. I think all of my comments are fairly minor.

I don't do many reviews in std, so it would probably be wise to get someone else to give this a sanity check as well. cc @rust-lang/libs

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.

Unfortunately I don't know the answer to this. This might require a git blame to ping the person who wrote them.

src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
@Lucretiel
Copy link
Contributor Author

Unfortunately I don't know the answer to this. This might require a git blame to ping the person who wrote them.

I've been doing git blame and it's been very helpful; usually the commit links to the specific github issue being resolved, which makes it easy to discover the specific intents. I'm cataloging that stuff in the "Tests" section.

@Lucretiel
Copy link
Contributor Author

All failing pre-existing tests have been updated, and the updates have been noted in the "Tests" section of the pull request description. Also added several new tests.

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-8 of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux x86_64-gnu-llvm-8
##[section]Starting: Initialize job
Agent name: 'Azure Pipelines 9'
Agent machine name: 'fv-az578'
Current agent version: '2.169.1'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https://github.com/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.3)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/de64ee8d-8219-4497-941f-4db1eaf55e77.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https://github.com/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/72808/merge:refs/remotes/pull/72808/merge
---
 ---> cb2676f08729
Step 5/8 : ENV RUST_CONFIGURE_ARGS       --build=x86_64-unknown-linux-gnu       --llvm-root=/usr/lib/llvm-8       --enable-llvm-link-shared       --set rust.thin-lto-import-instr-limit=10
 ---> Using cache
 ---> df25ce111862
Step 6/8 : ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy &&            python2.7 ../x.py test src/test/mir-opt --pass=build                                   --target=armv5te-unknown-linux-gnueabi &&            python2.7 ../x.py test src/tools/tidy
 ---> 599b9ac96b27
Step 7/8 : ENV NO_DEBUG_ASSERTIONS=1
 ---> Using cache
 ---> 091087e35a36
---
   Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
   Compiling chalk-rust-ir v0.10.0
   Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
   Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
   Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
   Compiling chalk-solve v0.10.0
   Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
   Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
   Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
   Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
   Compiling chalk-rust-ir v0.10.0
   Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
   Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
   Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
   Compiling chalk-solve v0.10.0
   Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
   Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
   Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
.................................................................................................... 1700/10277
...................................................................................i................ 1800/10277
.................................................................................................... 1900/10277
.................................................................................................... 2000/10277
....i..i............................................................................................ 2100/10277
..............................................................................................iiiii. 2200/10277
.................................................................................................... 2400/10277
.................................................................................................... 2500/10277
.................................................................................................... 2600/10277
.................................................................................................... 2700/10277
---
.........................i...............i.......................................................... 5200/10277
.................................................................................................... 5300/10277
.........................................................................i.......................... 5400/10277
...................................................................i................................ 5500/10277
...................................................................................ii.ii........i... 5600/10277
..........................i......................................................................... 5800/10277
..................................i................................................................. 5900/10277
........................................................................................ii.......... 6000/10277
...........................i........................................................................ 6100/10277
...........................i........................................................................ 6100/10277
.................................................................................................... 6200/10277
.................................................................................................... 6300/10277
..................................................ii...i..ii...........i............................ 6400/10277
.................................................................................................... 6600/10277
.................................................................................................... 6700/10277
.................................................................................................... 6700/10277
...................................................................................i..ii............ 6800/10277
..................................................test [ui] ui/mpsc_stress.rs has been running for over 60 seconds
.................................................................................................... 7000/10277
.................................................................................................... 7100/10277
.....................................i.............................................................. 7200/10277
.................................................................................................... 7300/10277
---
.................................................................................................... 8200/10277
.................................................................................................... 8300/10277
..........................................................................i......................... 8400/10277
.................................................................................................... 8500/10277
............................iiiiii.iiiiii.i......................................................... 8600/10277
.................................................................................................... 8800/10277
.................................................................................................... 8900/10277
.................................................................................................... 9000/10277
.................................................................................................... 9100/10277
---
Suite("src/test/codegen") not skipped for "bootstrap::test::Codegen" -- not in ["src/tools/tidy"]
Check compiletest suite=codegen mode=codegen (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 192 tests
iiii......i..............ii.i..........i......................i...........i..i................i....i 100/192
.............i.i.i...iii..iiiiiiiiiiiiiiii.......................iii................ii......

Suite("src/test/codegen-units") not skipped for "bootstrap::test::CodegenUnits" -- not in ["src/tools/tidy"]
 finished in 6.363
Check compiletest suite=codegen-units mode=codegen-units (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
Suite("src/test/assembly") not skipped for "bootstrap::test::Assembly" -- not in ["src/tools/tidy"]
Check compiletest suite=assembly mode=assembly (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 21 tests
iiiiiiiiiiiiiiiiiiiii

 finished in 0.173
Suite("src/test/incremental") not skipped for "bootstrap::test::Incremental" -- not in ["src/tools/tidy"]
Check compiletest suite=incremental mode=incremental (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
Suite("src/test/debuginfo") not skipped for "bootstrap::test::Debuginfo" -- not in ["src/tools/tidy"]
Check compiletest suite=debuginfo mode=debuginfo (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 115 tests
iiiii..i......i.i...i..i.i.i..i..i..ii....i.i....ii..........iiii.........i.....i..i.......ii.i.ii.. 100/115
...iiii.....ii.

 finished in 16.647
Suite("src/test/ui-fulldeps") not skipped for "bootstrap::test::UiFullDeps" -- not in ["src/tools/tidy"]
Uplifting stage1 rustc (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---

   Doc-tests core

running 2577 tests
......iiiii......................................................................................... 100/2577
.................................................................................................ii. 200/2577
......................................i............................................................. 400/2577
..............................................................................................i..i.. 500/2577
..............................................................................................i..i.. 500/2577
................iiii................................................................................ 600/2577
.................................................................................................... 800/2577
.................................................................................................... 900/2577
.................................................................................................... 1000/2577
.................................................................................................... 1100/2577
---
....thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: RecvError', src/libstd/sync/mpsc/mod.rs:2034:21
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: "SendError(..)"', src/libstd/sync/mpsc/mod.rs:2022:17
.......thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: RecvError', src/libstd/sync/mpsc/mod.rs:1916:13
. 600/777
.....................................thread '<unnamed>' panicked at 'explicit panic', src/libstd/sync/mutex.rs:634:13
..thread '<unnamed>' panicked at 'test panic in inner thread to poison mutex', src/libstd/sync/mutex.rs:588:13
...thread '...<unnamed>' panicked at 'test panic in inner thread to poison mutex', src/libstd/sync/mutex.rs:564:13
..thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `1`,
 right: `2`', src/libstd/sync/mutex.rs:658:13
.........thread '<unnamed>' panicked at 'test panic in inner thread to poison RwLock', src/libstd/sync/rwlock.rs:789:13
---
    io::buffered::tests::line_vectored

test result: FAILED. 776 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '-p std --lib'

command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "test" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-j" "2" "--release" "--locked" "--color" "always" "--features" "panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/checkout/src/libtest/Cargo.toml" "-p" "std" "--" "--quiet"
expected success, got: exit code: 101

---
  local time: Tue Jun  2 07:22:58 UTC 2020
  network time: Tue, 02 Jun 2020 07:22:58 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/72808/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/72808/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (3573) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
@Lucretiel
Copy link
Contributor Author

@BurntSushi can you detach #60673 from this issue? I don't want it to be incorrectly closed if/when this is merged.

@mark-i-m
Copy link
Member

mark-i-m commented Jun 2, 2020

@Lucretiel you might be able to unattach it by editing your comment to break github's pattern matching, e.g. misspell resolve or something... it's bit hacky but I think it could work...

@Lucretiel
Copy link
Contributor Author

@Lucretiel you might be able to unattach it by editing your comment to break github's pattern matching, e.g. misspell resolve or something... it's bit hacky but I think it could work...

That fixed it, thank you!

src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
@Elinvynia Elinvynia added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 10, 2020
@Elinvynia Elinvynia added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 25, 2020
@Dylan-DPC-zz
Copy link

r? @Amanieu

@rust-highfive rust-highfive assigned Amanieu and unassigned BurntSushi Jun 26, 2020
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
src/libstd/io/buffered.rs Outdated Show resolved Hide resolved

// This is what we're going to try to write directly to the inner
// writer. The rest will be buffered, if nothing goes wrong.
let lines = &buf[..newline_idx];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can just write the entire buf directly to the inner writer. AFAIK LineWriter doesn't make any guarantees that partial lines are actually buffered, it only guarantees that newlines will be written out immediately instead of being buffered.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there are multiple concurrent writers to a pipe it is still useful to buffer up whole lines to avoid interleaving text. This is a common problem with multithreaded programs printing to stdout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly considered that. However, in my opinion, taking steps to only write whole lines is the least surprising behavior; my expectation as a client of this API would be that (outside of very-long lines) it only flushes whole lines, regardless of the shape of the input.

@Lucretiel
Copy link
Contributor Author

Received the review, will get to it as soon as I'm able

src/libstd/io/buffered.rs Outdated Show resolved Hide resolved
@workingjubilee
Copy link
Contributor

workingjubilee commented Aug 14, 2020

I should clarify and correct that the target in question appears to be

Step 16/19 : ENV TARGETS=wasm32-unknown-emscripten

Which appears to bind inside sys::unix to os::emscripten
Likewise libc::unix::linux_like::emscripten declares

pub const EBADF: ::c_int = 8;

HOWEVER that was a recent change
and std is currently pinned to the version:

libc = { version = "0.2.51", default-features = false, features = ['rustc-dep-of-std'] }

So I opened #75515 to bump it to the latest. Mind, that shouldn't affect it due to the Cargo.lock using 0.2.74 already, but at this point I'm not sure we don't have to check the world isn't going mad.

@Manishearth
Copy link
Member

@Lucretiel the test cannot fail on current master, because current master is always a commit that has passed tests.

I'm really not the right person to talk to about this, though.

@Lucretiel
Copy link
Contributor Author

Just to note here: it appears (via @tmiasko) that this test is currently succeeding improperly, with a description and fix in #75532. The core of the issue is that currently, LineWriter is swallowing errors that this test should be detecting. My change in this PR, which removed error-swallowing behavior, caused the test to start failing for wasi, which I believe to be because it's raising error code 9 instead of 8 for EBADF.

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Aug 16, 2020
Bump minor version of emsdk to 1.38.47

Release Notes:

```
v1.38.47: 10/02/2019
--------------------
 - Add support for FETCH API in WASM backend. This doesn't support FETCH in the
   main thread (`USE_FETCH_WORKER=0` is enforced). rust-lang#9490
 - Redefine errno values to be consistent with wasi. This will let us avoid
   needing to convert the values back and forth as we use more wasi APIs.
   This is an ABI change, which should not be noticeable from user code
   unless you use errno defines (like EAGAIN) *and* keep around binaries
   compiled with an older version that you link against. In that case, you
   should rebuild them. See rust-lang#9545.
 - Removed build option `-s ONLY_MY_CODE` as we now have much better solutions
   for that, like building to a wasm object file or using `STANDALONE_WASM`
   etc. (see
   https://github.com/emscripten-core/emscripten/wiki/WebAssembly-Standalone).
 - Emscripten now supports the config file (.emscripten) being placed in the
   emscripten directory rather that the current user's home directory.
   See rust-lang#9543
```

Motivated by changes to errno values which are currently out of sync with those
in libc crate which uses wasi values already. Helps with rust-lang#72808 and rust-lang#75532.
tmandry added a commit to tmandry/rust that referenced this pull request Aug 16, 2020
Bump minor version of emsdk to 1.38.47

Release Notes:

```
v1.38.47: 10/02/2019
--------------------
 - Add support for FETCH API in WASM backend. This doesn't support FETCH in the
   main thread (`USE_FETCH_WORKER=0` is enforced). rust-lang#9490
 - Redefine errno values to be consistent with wasi. This will let us avoid
   needing to convert the values back and forth as we use more wasi APIs.
   This is an ABI change, which should not be noticeable from user code
   unless you use errno defines (like EAGAIN) *and* keep around binaries
   compiled with an older version that you link against. In that case, you
   should rebuild them. See rust-lang#9545.
 - Removed build option `-s ONLY_MY_CODE` as we now have much better solutions
   for that, like building to a wasm object file or using `STANDALONE_WASM`
   etc. (see
   https://github.com/emscripten-core/emscripten/wiki/WebAssembly-Standalone).
 - Emscripten now supports the config file (.emscripten) being placed in the
   emscripten directory rather that the current user's home directory.
   See rust-lang#9543
```

Motivated by changes to errno values which are currently out of sync with those
in libc crate which uses wasi values already. Helps with rust-lang#72808 and rust-lang#75532.
Comment on lines 1136 to 1144
// Flush existing content to prepare for our write
self.buffer.flush_buf()?;

// This is what we're going to try to write directly to the inner
// writer. The rest will be buffered, if nothing goes wrong.
let (lines, tail) = buf.split_at(newline_idx + 1);

// Write `lines` directly to the inner writer, bypassing the buffer.
self.inner_mut().write_all(lines)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using println!, one desirable property is that the formatted output is
written using a single syscall. To take advantage of write atomicity, and avoid
interleaving the output with that from other processes that might be sharing
the same stdout.

The implementation here performs two syscalls whenever there are any formatting
operations in println!. The presence of terminal \n triggers the flush of
existing buffer content and then the final string with a newline is flushed
separately.

For example, println!("Hello {}!", "world");, leads to:

write(1, "Hello world", 11)             = 11
write(1, "!\n", 2)                      = 2

Copy link
Contributor Author

@Lucretiel Lucretiel Aug 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. It's a good point. The problem is this:

Suppose that, as a solution (in your example), we buffer the second print, so that we can do the whole thing in a single write. At that point, we are obligated to return an Ok(2), because we have "successfully" written 2 bytes (by adding them to the buffer). This means that we can't actually attempt to send the byte to the underlying device, because if the device returns an error, we'd have to ignore that error (or save it and return it on the next write), which (along with extra, unnecessary data buffering) is something I was trying to avoid.

One workaround to this would be to buffer the data, attempt a write, and if the write returns an error, remove that data from the buffer. However, in that case, if the first write returns Ok(12) (representing "Hello World!") and the second write returns an error, there's no way to report a consistent result to the caller without also suppressing that error.

That being said, this problem probably can be addressed in the write_all specialization (used by default write_fmt), since it's not necessary to try to preserve a recoverable / knowable state in the event of an error there, like it is with write. I'll see about retooling write_all a bit to address this concern.

Copy link
Contributor Author

@Lucretiel Lucretiel Aug 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! I've updated write_all to address this concern, and added a test verifying the behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing this! I expected that doing so might be challenging in
context of other goals. Fortunately, the fact that formatting goes through
write_all made it quite manageable. Nice!

`LineWriter::write_all` now only emits a single write when writing a
newline when there's already buffered data.
@Amanieu
Copy link
Member

Amanieu commented Aug 28, 2020

@bors r+

@bors
Copy link
Contributor

bors commented Aug 28, 2020

📌 Commit c91e764 has been approved by Amanieu

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 28, 2020
@bors
Copy link
Contributor

bors commented Aug 28, 2020

⌛ Testing commit c91e764 with merge 7b1dd61...

@bors bors mentioned this pull request Aug 29, 2020
@bors
Copy link
Contributor

bors commented Aug 29, 2020

☀️ Test successful - checks-actions, checks-azure
Approved by: Amanieu
Pushing 7b1dd61 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Aug 29, 2020
@bors bors merged commit 7b1dd61 into rust-lang:master Aug 29, 2020
@Lucretiel
Copy link
Contributor Author

🤯🤯🤯🤯 we actually got the thing merged!

@ecstatic-morse
Copy link
Contributor

It's not quite big enough to warrant inclusion in the perf triage report for this week, but the encoding-rs benchmark, which results in quite a few errors being emitted, was slightly improved by this PR.

@Lucretiel
Copy link
Contributor Author

Oh, neat! If I had to guess it probably had to do more with the write_all optimiations than the LineWriter stuff, but a win is a win!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

LineWriter should not fully flush its lines