Skip to content

concurrent rustdoc usage can lead to bad times #30220

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

Open
letheed opened this issue Dec 5, 2015 · 5 comments
Open

concurrent rustdoc usage can lead to bad times #30220

letheed opened this issue Dec 5, 2015 · 5 comments
Labels
C-bug Category: This is a bug. E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Comments

@letheed
Copy link
Contributor

letheed commented Dec 5, 2015

I noticed that the documentation generated by cargo doc was not exactly the same depending on if I ran cargo test or cargo build first.

Running:

$ cargo clean
$ cargo doc
$ mv target/doc tdoc
$ cargo clean
$ cargo test # or cargo build
$ cargo doc
$ diff -r tdoc target/doc
  • in search-index.js: the indexes for my two targets (a [lib] and a [[bin]]) are swapped. The two lines are identical but since they're swapped git detects a file modification when my script copies the doc over to the gh-pages branch, and the file is commited for nothing.
diff -r tdoc/search-index.js target/doc/search-index.js
2d1
< searchIndex['takuzu'] = {"items":[[0,"","takuzu","A Takuzu (a.k.a. Binairo) solving library.",null,null],[3,"Grid","","A container for takuzu grid manipulation.",null,null],[4,"GridError","","An error returned when checking if the grid is well-sized and legal.",null,null],[13,"BadSize","","The grid does not have the right size.",0,null],[13,"Illegal","","The grid is illegal, that is it infringes at least one of the rules.",0,null],[4,"GridParseError","","An error returned when parsing a string to create a grid failed.",null,null],[13,"CreationError","","A `Grid` cannot be created from this `Array`.",1,null],[13,"UnexpectedCharacter","","At least one character other than `0`, `1`, `.` or '\\n'\nwas found in the string.",1,null],[4,"GridSizeError","","An error returned when the grid is not properly sized.",null,null],[13,"Empty","","The grid is empty.",2,null],[13,"NotASquare","","The grid is not a square.\nThe field contains the line number that triggered the error.",2,null],[13,"OddRowNumber","","The grid has an odd number of rows.",2,null],[4,"SourceError","","An error returned by the `source` method when either reading or parsing failed.",null,null],[13,"IO","","Reading from the source failed.",3,null],[13,"Parsing","","Parsing failed.",3,null],[11,"eq","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"ne","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"hash","","",0,null],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"griderror"}}],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"str"}}],[11,"cause","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"option"}}],[11,"from","","",0,{"inputs":[{"name":"griderror"},{"name":"gridsizeerror"}],"output":{"name":"self"}}],[11,"eq","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"ne","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"hash","","",1,null],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"gridparseerror"}}],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"str"}}],[11,"cause","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"option"}}],[11,"eq","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"ne","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"hash","","",2,null],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"gridsizeerror"}}],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"str"}}],[11,"eq","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"ne","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"hash","","",4,null],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",4,{"inputs":[{"name":"grid"}],"output":{"name":"grid"}}],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"from_str","","",4,{"inputs":[{"name":"grid"},{"name":"str"}],"output":{"name":"result"}}],[11,"index","","",4,null],[11,"index_mut","","",4,null],[11,"new","","Creates a `Grid` from a preexisting array.",4,{"inputs":[{"name":"grid"},{"name":"array"}],"output":{"name":"result"}}],[11,"size","","Returns the size of the grid.",4,{"inputs":[{"name":"grid"}],"output":{"name":"usize"}}],[11,"into_inner","","Consumes a `Grid` and returns the underlying array.",4,{"inputs":[{"name":"grid"}],"output":{"name":"array"}}],[11,"is_filled","","Returns `true` if the grid contains no empty cell.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_legal","","Verifies that the grid does not currently violate any of the rules.\nReturns `true` if the grid is legal.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_cell_legal","","Verifies that a certain cell does not violate any of the rules.\nReturns `true` if the value is legal.",4,{"inputs":[{"name":"grid"},{"name":"usize"},{"name":"usize"}],"output":{"name":"bool"}}],[11,"next_empty","","Returns the index of the first empty cell or None if the grid is filled.",4,{"inputs":[{"name":"grid"}],"output":{"name":"option"}}],[11,"apply_rules","","Skims through the grid once for each rule and fills in the blanks\nwhere the value is unambiguous.\nReturns `true` if the grid was modified.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"solve","","Solves the grid using both rules logic and a backtracking algorithm,\nand returns an array containing the solution(s).\nIf no solution exists, an empty array is returned.",4,{"inputs":[{"name":"grid"}],"output":{"name":"vec"}}],[11,"to_string_diff","","Suitable for terminals.",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"string"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"str"}}],[11,"cause","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"option"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"ioerror"}],"output":{"name":"self"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"gridparseerror"}],"output":{"name":"self"}}],[6,"Array","","A raw takuzu grid representation.",null,null],[8,"Source","","The `Source` trait allows to use any implementor of the `Read` trait\nas an input source for the grid string format with no additional effort.",null,null],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}]],"paths":[[4,"GridError"],[4,"GridParseError"],[4,"GridSizeError"],[4,"SourceError"],[3,"Grid"],[8,"Source"]]};
3a3
> searchIndex['takuzu'] = {"items":[[0,"","takuzu","A Takuzu (a.k.a. Binairo) solving library.",null,null],[3,"Grid","","A container for takuzu grid manipulation.",null,null],[4,"GridError","","An error returned when checking if the grid is well-sized and legal.",null,null],[13,"BadSize","","The grid does not have the right size.",0,null],[13,"Illegal","","The grid is illegal, that is it infringes at least one of the rules.",0,null],[4,"GridParseError","","An error returned when parsing a string to create a grid failed.",null,null],[13,"CreationError","","A `Grid` cannot be created from this `Array`.",1,null],[13,"UnexpectedCharacter","","At least one character other than `0`, `1`, `.` or '\\n'\nwas found in the string.",1,null],[4,"GridSizeError","","An error returned when the grid is not properly sized.",null,null],[13,"Empty","","The grid is empty.",2,null],[13,"NotASquare","","The grid is not a square.\nThe field contains the line number that triggered the error.",2,null],[13,"OddRowNumber","","The grid has an odd number of rows.",2,null],[4,"SourceError","","An error returned by the `source` method when either reading or parsing failed.",null,null],[13,"IO","","Reading from the source failed.",3,null],[13,"Parsing","","Parsing failed.",3,null],[11,"eq","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"ne","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"hash","","",0,null],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"griderror"}}],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"str"}}],[11,"cause","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"option"}}],[11,"from","","",0,{"inputs":[{"name":"griderror"},{"name":"gridsizeerror"}],"output":{"name":"self"}}],[11,"eq","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"ne","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"hash","","",1,null],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"gridparseerror"}}],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"str"}}],[11,"cause","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"option"}}],[11,"eq","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"ne","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"hash","","",2,null],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"gridsizeerror"}}],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"str"}}],[11,"eq","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"ne","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"hash","","",4,null],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",4,{"inputs":[{"name":"grid"}],"output":{"name":"grid"}}],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"from_str","","",4,{"inputs":[{"name":"grid"},{"name":"str"}],"output":{"name":"result"}}],[11,"index","","",4,null],[11,"index_mut","","",4,null],[11,"new","","Creates a `Grid` from a preexisting array.",4,{"inputs":[{"name":"grid"},{"name":"array"}],"output":{"name":"result"}}],[11,"size","","Returns the size of the grid.",4,{"inputs":[{"name":"grid"}],"output":{"name":"usize"}}],[11,"into_inner","","Consumes a `Grid` and returns the underlying array.",4,{"inputs":[{"name":"grid"}],"output":{"name":"array"}}],[11,"is_filled","","Returns `true` if the grid contains no empty cell.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_legal","","Verifies that the grid does not currently violate any of the rules.\nReturns `true` if the grid is legal.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_cell_legal","","Verifies that a certain cell does not violate any of the rules.\nReturns `true` if the value is legal.",4,{"inputs":[{"name":"grid"},{"name":"usize"},{"name":"usize"}],"output":{"name":"bool"}}],[11,"next_empty","","Returns the index of the first empty cell or None if the grid is filled.",4,{"inputs":[{"name":"grid"}],"output":{"name":"option"}}],[11,"apply_rules","","Skims through the grid once for each rule and fills in the blanks\nwhere the value is unambiguous.\nReturns `true` if the grid was modified.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"solve","","Solves the grid using both rules logic and a backtracking algorithm,\nand returns an array containing the solution(s).\nIf no solution exists, an empty array is returned.",4,{"inputs":[{"name":"grid"}],"output":{"name":"vec"}}],[11,"to_string_diff","","Suitable for terminals.",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"string"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"str"}}],[11,"cause","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"option"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"ioerror"}],"output":{"name":"self"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"gridparseerror"}],"output":{"name":"self"}}],[6,"Array","","A raw takuzu grid representation.",null,null],[8,"Source","","The `Source` trait allows to use any implementor of the `Read` trait\nas an input source for the grid string format with no additional effort.",null,null],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}]],"paths":[[4,"GridError"],[4,"GridParseError"],[4,"GridSizeError"],[4,"SourceError"],[3,"Grid"],[8,"Source"]]};
  • in fn.solve_from.html (which is a public function in the [[bin]] crate): the trait bound on the parametric type does not link over to its own documentation in the [lib] crate any more:
diff -r tdoc/tackle/fn.solve_from.html target/doc/tackle/fn.solve_from.html
49c49
< <pre class='rust fn'>pub fn solve_from&lt;T: Source + ?<a class='trait' href='https://doc.rust-lang.org/nightly/core/marker/trait.Sized.html' title='core::marker::Sized'>Sized</a>&gt;(source: &amp;mut T)</pre><div class='docblock'><p>Reads a grid from a source, triggers the solving algorithm

---
> <pre class='rust fn'>pub fn solve_from&lt;T: <a class='trait' href='../takuzu/source/trait.Source.html' title='takuzu::source::Source'>Source</a> + ?<a class='trait' href='https://doc.rust-lang.org/nightly/core/marker/trait.Sized.html' title='core::marker::Sized'>Sized</a>&gt;(source: &amp;mut T)</pre><div class='docblock'><p>Reads a grid from a source, triggers the solving algorithm

The first point is not that big of a problem but the second one is clearly a bug.

As suggested by @alexcrichton, using the --jobs 1 flag on both cargo doc and cargo build/test solves the problem by making the process deterministic, at the expense of compile/docgen time obviously.

@lucab
Copy link
Contributor

lucab commented Dec 10, 2015

We have just observed the same (non-deterministic JSON ordering) during reproducible rebuilds in Debian. In general, it would be a good idea to make the serialization deterministic.

@sfackler
Copy link
Member

This should be filed in the rust-lang/cargo repo.

@letheed
Copy link
Contributor Author

letheed commented Dec 11, 2015

@sfackler actually I did that in the first place but I was told to open a new one here. The original bug report can be found here

@alexcrichton
Copy link
Member

Yes I think this is likely related to the nondeterminism of running multiple processes at once and I think that rustdoc can probably add more safeguards/sorting rather than Cargo handling this (unless I'm misunderstanding)

@steveklabnik steveklabnik added the T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. label Jun 6, 2016
@steveklabnik steveklabnik changed the title cargo test/build interferes with cargo doc concurrent rustdoc usage can lead to bad times Jun 6, 2016
bors added a commit that referenced this issue Dec 7, 2016
…omez

rustdoc: Sort lines in search index and implementors js

This means the files are generated deterministically even with rustdoc running in parallel.

Fixes the first part of #30220.
@steveklabnik steveklabnik added T-dev-tools Relevant to the dev-tools subteam, which will review and decide on the PR/issue. and removed T-tools labels May 18, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 24, 2017
@steveklabnik
Copy link
Member

Triage: is anyone still seeing this issue? Any way of reproducing it more easily?

@camelid camelid added the E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example label Feb 1, 2021
@ehuss ehuss removed the T-dev-tools Relevant to the dev-tools subteam, which will review and decide on the PR/issue. label Jan 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

8 participants