Goobley Goobley - 3 months ago 12
C++ Question

C++ stdin occasionally garbled

I've been experiencing a strange occasionally occurring bug for the last few days.

I have a console application that also displays a window opened with SDL for graphical output continuously running three threads. The main thread runs the event loop, and processes the console input. The second thread uses

std::cin.getline
to get the console input. This second thread, however, is also responsible for outputting logging information, which can be produced when the user clicks somewhere on the SDL window.

These log messages are sent to a mutex-protected
stringstream
regularly checked by thread 2. If there are log messages it deletes the prompt, outputs them and then prints a new prompt. Due to this it can't afford to block on
getline
, so this thread spawns the third thread that
peek
s
cin
and signals via an
atomic
when there's data to be got from the input stream, at which point
getline
is called and the input is passed to the logic on the main thread.

Here's the bit I haven't quite worked out, about 1 in 30 of these fails since the program doesn't receive exactly the same input as was typed into the terminal. You can see what I mean in the images here, the first line is what was type and the second is the Lua stacktrace due to receiving different (incorrect) input.

This occurs whether I use
rlwrap
or not. Is this due to
peek
and
getline
hitting the input stream at the same time? (This is possible as the
peek
loop just looks like:

while(!exitRequested_)
{
if (std::cin.peek())
inputAvailable_ = true; // this is atomic

std::this_thread::sleep_for(std::chrono::milliseconds(10));
}


Any thoughts? I looked at curses quickly, but it looks like quite a lot of effort to use. I've never heard of get line garbling stuff before. But I also printed every string that was received for a while and they matched what Lua is reporting.

Answer

As @davmac suggested, peek appears to have been interfering with getline. My assumption would be that this is linked to peek taking a character and then putting it back at the same time as getline takes the buffer.

Whatever the underlying cause of the issue is, I am >98% sure that the problem has been fixed by implementing the fix davmac suggested. In several hours of use I have had no issues.

Moral, don't concurrently access cin, even if one of the functions doesn't modify the stream.

(Note, the above happened on both g++ and clang++ so I assume that it's just linked to the way the std library is frequently implemented).

As @DavidSchwartz pointed out, concurrent access to streams is explicitly prohibited, so that clearly explains why the fix works.