Search code examples
c++stlc++20iostream

std::osyncstream outputs garbled text and causes seg fault


This code when using osyncstream outputs garbage characters, isn't alway in sync, and seg faults. When output is to std::cout directly the output isn't in sync but output is good and does not seg fault.

#include <atomic>
#include <chrono>
#include <iostream>
#include <syncstream>
#include <thread>

std::osyncstream sync_out { std::cout };
//std::ostream& sync_out { std::cout };

void test_ths(int th) {

    while(true) {
        sync_out << "th " << th << " wait 1\n";
//        std::this_thread::sleep_for(std::chrono::milliseconds(30 + 2 * th));
        std::this_thread::sleep_for(std::chrono::milliseconds(30));

        sync_out << "th " << th << " wait 2\n";
//        std::this_thread::sleep_for(std::chrono::milliseconds(30 + 2 * th));
        std::this_thread::sleep_for(std::chrono::milliseconds(30));

        // needed to force output from osyncstream
        sync_out.emit();  // comment out when using std::cout
    }
}
int main() {

    std::jthread t1([] {
        test_ths(1);
    });

    std::jthread t2([] {
        test_ths(2);
    });

    std::jthread t3([] {
        test_ths(3);
    });

    std::jthread t4([] {
        test_ths(4);
    });

    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

Sample output illustrating problems.

th 2 wait 1
th th 4 wait 2
3 wait 2
th 1 wait 2
th 2 wait 2
th t 1
th о�ޓ�F�ߓ�F�@FfW��,@���W�th 4 wait 1
th  wait 1
th 2 wait 1
th 3 wait 2
th 4 wait 2
th 2 wait 2
th 3 wait 2
th 1 wait 2
th 2 wait 1
th 4 wait 1
th 3 wait 1
Segmentation fault (core dumped)

The problem is more pronounced when the sleep_for times are the same. The commented out lines introduce some jitter which helps but the problems still occur.

When std::cout is used, with sync_out.emit(); commented out, it runs okay.

My understanding of osyncstream is the output from different thread shouldn't intermingle, i.e. that is its purpose.


Solution

  • That's not how osyncstream is supposed to be used. Every thread needs to construct its own osyncstream; there is no synchronization on access to the osyncstream itself. Only the transfer performed by emit is synchronized, and then only with respect to the streambuf it wraps.

    Having a global osyncstream is therefore entirely pointless.