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

Speculative fix for vroom issue #512

Merged
merged 3 commits into from Sep 21, 2023
Merged

Conversation

DavisVaughan
Copy link
Member

@DavisVaughan DavisVaughan commented Sep 18, 2023

Fixes #510

I don't think we should merge this yet, but I do think it should fix the issue.

Has something to do with the fact that std::async will force a copy of the arguments you pass it, even if they ultimately get called in f as const reference, because std::async has no way of knowing that.

So to perfect forward a const reference, the right way to do it is to wrap the thing you want a reference to in std::ref() and pass that along instead.

I am not entirely sure why this broke with new cpp11. Probably some cpp11 destructor is being run (on the thread?) that unprotects the underlying SEXP too early.

https://stackoverflow.com/questions/18359864/passing-arguments-to-stdasync-by-reference-fails
https://www.linkedin.com/pulse/careful-when-using-const-reference-stdthread-stdasync-bhavith-c-achar

@@ -381,7 +381,7 @@ void vroom_write_out(
auto end = begin + num_lines;
futures[idx][t++] = std::async(
fill_buf,
input,
std::ref(input),
Copy link
Member

Choose a reason for hiding this comment

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

Thanks, Davis! This has a great ratio of "size of fix" to "size of headache / anxiety about problem" 😅

@DavisVaughan
Copy link
Member Author

DavisVaughan commented Sep 18, 2023

My guess as to what happens is:

  • The input was being copied as it was sent to the thread, and stored in the thread "shared state"
  • The copy is destroyed at a later time, likely at the get() time, due to being part of the thread "shared state"

Now, these get() calls end up happening in another async function

auto buf = futures[idx][i].get();

Which means that, at least in theory, I think future[0] could be calling get() and running a cpp11 destructor (doing a cpp11 release()) at the same time future[1] is copying input (doing a cpp11 insert()). Because the insert/delete behavior share the same underlying SEXP preserve list, I think we are occasionally accessing it at the exact same time across threads, which isn't safe.

Very good explanation of async argument destruction time:
https://stackoverflow.com/questions/47436919/why-is-passing-by-const-ref-slower-when-using-stdasync


I am still not 100% sure why fiddling with the preserve list on the cpp11 side (that commit where stuff started breaking) triggered this to show up, but I am now fairly confident that vroom is where the fix should come from.

@jennybc
Copy link
Member

jennybc commented Sep 21, 2023

It feels like I should merge this @DavisVaughan and move towards release. How do you feel about that?

@DavisVaughan
Copy link
Member Author

Yes I agree

@jennybc jennybc merged commit 2f96e7b into tidyverse:main Sep 21, 2023
13 checks passed
MJochim added a commit to IPS-LMU/emuR that referenced this pull request Sep 26, 2023
We started seeing random-looking test failures on CRAN on August 17. We
have now found out that on our Windows machine, write_lines with
multiple threads and cause R to hang forever. This happens only with
some input data, and even with the same data only in one of about 300
cases.

This would also be fixed with the very recent change to readr/vroom:
tidyverse/vroom#512

But that fix is not on CRAN yet.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Segfault is appearing in vroom in other package's revdep checks
2 participants