Bug 89740 - reading from cin & writing to cout sync_with_stdio(false) has race conditions
Summary: reading from cin & writing to cout sync_with_stdio(false) has race conditions
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 7.3.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-03-16 22:39 UTC by bert hubert
Modified: 2021-11-19 12:01 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
a small reproduction for this bug. (238 bytes, text/x-csrc)
2019-03-16 22:39 UTC, bert hubert
Details
reproduction that actually shows the bug (240 bytes, text/x-csrc)
2019-03-16 22:50 UTC, bert hubert
Details

Note You need to log in before you can comment on or make changes to this bug.
Description bert hubert 2019-03-16 22:39:47 UTC
Created attachment 45981 [details]
a small reproduction for this bug.

Reading lots of lines from cin in main() and writing to cout from an additional thread usually works fine. However, when doing sync_with_stdio(false), duplicate output appears on the terminal. No C stdio is performed by the program. No two threads are attempting simultaneous output. 

Output of attached 25 line reproduction:

$ yes | ./repro
HHi 0
Hi Hi 1
Hi Hi 2

Verification with strace shows that the main thread is doing writes to file descriptor 1 that are also happening in the output thread.

The problem goes away when setting 'cin.tie(nullptr)' or when removing sync_with_stdio(false):
$ yes | ./repro
Hi 0
Hi 1
Hi 2

A trivial reproduction is attached.

The hypothesis is that cin/cout tying is somehow relying on sync_with_stdio to prevent race conditions.

Bug #70276 touches on slightly similar material, but it looks like something different.
Comment 1 bert hubert 2019-03-16 22:47:55 UTC
Comment on attachment 45981 [details]
a small reproduction for this bug.

>#include <iostream>
>#include <thread>
>#include <string>
>#include <unistd.h>
>
>using namespace std;
>
>void theThread()
>{
>  for(int counter = 0 ;; ++counter) {
>    usleep(250000);
>    cout << "Hi "<< counter << endl;
>  }
>}
>
>int main()
>{
>  std::ios_base::sync_with_stdio(false);
>  // cin.tie(nullptr);
>  
>  string line;
>  thread t(theThread);
>  while(getline(cin, line))
>    ;
>}
Comment 2 bert hubert 2019-03-16 22:50:30 UTC
Created attachment 45982 [details]
reproduction that actually shows the bug

This is the version of the reproduction that exhibits the problem. The other version had the "fix" included, cin.tie(nullptr), and thus did not have the problem.
Comment 3 bert hubert 2019-03-16 23:34:19 UTC
Note that in https://stackoverflow.com/questions/12605725/basic-thread-locking-in-c11/12608911#12608911 we read the calling flush() from two threads at the same time is not legal if the streams are unsynchronised.

The tie() may be causing this unexpectedly.
Comment 4 bert hubert 2019-03-17 20:50:16 UTC
I did a longer writeup here, where I tentatively conclude this is not a bug, just highly unfortunate: https://ds9a.nl/articles/posts/iostreams-unexpected/
Comment 5 Jonathan Wakely 2021-11-19 12:01:14 UTC
(In reply to bert hubert from comment #4)
> I did a longer writeup here, where I tentatively conclude this is not a bug,
> just highly unfortunate: https://ds9a.nl/articles/posts/iostreams-unexpected/

Agreed.