cmannett85
cmannett85

Reputation: 22376

std::stringstream across threads doesn't work as expected

I'm sending streaming data between threads using std::iostreams but struggling to extract the data, here's a contrived example:

#include <sstream>
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>

using namespace std::chrono_literals;

int main()
{
    auto stream = std::stringstream{};
    auto stop = std::atomic_bool{false};
    auto mtx = std::mutex{};

    auto thread = std::thread{[&]() {
        while(!stop) {
            auto lock = std::lock_guard{mtx};
            if (stream.peek() != decltype(stream)::traits_type::eof()) {
                auto str = std::string{};
                stream >> str;
                std::cout << str;
            }
        }
    }};

    {
        // Make sure thread is running before sending data
        std::this_thread::sleep_for(100ms);
        {
            auto lock = std::lock_guard{mtx};
            stream << "hello" << std::endl;
        }

        // Give the thread a chance to receive it
        std::this_thread::sleep_for(100ms);
    }

    stop = true;
    thread.join();

    return EXIT_SUCCESS;
}

Running it, yields no output - debugging shows that stream.peek() is always EOF. I must be doing something wrong but I can't see it!

Upvotes: 0

Views: 452

Answers (1)

Marek R
Marek R

Reputation: 38209

Problem is caused by fact that you are reading data before there is anything to read (it was written).

When you have tried to read data from empty stream error flags are getting set and then any following read/write operation on such stream are not executed until error flags are cleared.

You need something what will ensure that reading will happen after writing. std::condition_variable can do that.

https://godbolt.org/z/fs9TGW

int main()
{
    auto stream = std::stringstream{};
    auto mtx = std::mutex{};
    auto hasData = std::condition_variable{};
    auto readingStarted = std::condition_variable{};

    auto thread = std::thread{[&]() {
        std::unique_lock<std::mutex> lock{mtx};
        readingStarted.notify_one();
        hasData.wait(lock);
        auto str = std::string{};
        stream >> str;
        std::cout << str;
    }};

    {
        auto lock = std::unique_lock{mtx};
        readingStarted.wait(lock);
        stream << "hello" << std::endl;
        hasData.notify_one();
    }

    thread.join();

    return EXIT_SUCCESS;
}

Upvotes: 2

Related Questions