1

I'm trying to understand C++ coroutines. My expectation in the example below would be, that each asio::post will switch the context/thread to the given threadpool. But something very strange happens. I get the following output:

thread0: 7f0239bfb3c0
thread2: 7f02391f6640 (tp2)
thread3: 7f02387f5640 (tp)
thread4: 7f02387f5640 (tp2)
thread5: 7f02373f3640 (tp)
thread6: 7f02373f3640 (tp2)
thread7: 7f0235ff1640 (tp)
done

So the thread-id of 3 and 4 are the same, but they should run on a different context/threadpool. And 3, 5 and 7 should have the same ID since it's the same context (with just one thread). I assume that I understand some concept wrong. Can you give me a hint?

Thanks

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>

boost::asio::thread_pool tp {1};
boost::asio::thread_pool tp2 {10};

boost::asio::awaitable<void> test() {
  std::cout << "thread2: " << boost::this_thread::get_id() << std::endl;
  co_await boost::asio::post(tp, boost::asio::use_awaitable);
  std::cout << "thread3: " << boost::this_thread::get_id() << std::endl;
  co_await boost::asio::post(tp2, boost::asio::use_awaitable);
  std::cout << "thread4: " << boost::this_thread::get_id() << std::endl;
  co_await boost::asio::post(tp, boost::asio::use_awaitable);
  std::cout << "thread5: " << boost::this_thread::get_id() << std::endl;
  co_await boost::asio::post(tp2, boost::asio::use_awaitable);
  std::cout << "thread6: " << boost::this_thread::get_id() << std::endl;
  co_await boost::asio::post(tp, boost::asio::use_awaitable);
  std::cout << "thread7: " << boost::this_thread::get_id() << std::endl;
}

int main() {
  std::cout << "thread0: " << boost::this_thread::get_id() << std::endl;

  boost::asio::co_spawn(tp2, &test, [](std::exception_ptr e) { std::cout << "done" << std::endl; });

  tp2.join();
  tp.join();
}
Spide
  • 89
  • 5

1 Answers1

1

I have a near identical answer up here: asio How to change the executor inside an awaitable?

I'll leave the explanation there, but your code can act as expected like this:

Live On Coliru

#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <thread>
namespace asio = boost::asio;

asio::thread_pool tp1{1}, tp2{1};

static inline auto trace(auto msg) {
    static std::mutex mx;
    std::lock_guard lg(mx);
    std::cout //
        << "trace " << msg << ": thread " << std::hex << std::setw(2)
        << std::setfill('0')
        << (std::hash<std::thread::id>{}(std::this_thread::get_id()) % 256)
        << std::endl;
}

asio::awaitable<void> test() {
    trace(2);
    auto a1 = asio::bind_executor(tp1.get_executor(), asio::use_awaitable);
    auto a2 = asio::bind_executor(tp2.get_executor(), asio::use_awaitable);
    co_await post(a1); trace(3);
    co_await post(a2); trace(4);
    co_await post(a1); trace(5);
    co_await post(a2); trace(6);
    co_await post(a1); trace(7);
}

int main() {
    post(tp1, [] { trace("tp1"); });
    post(tp2, [] { trace("tp2"); });

    co_spawn(tp2, test, [](std::exception_ptr const&) {
        std::cout << "done" << std::endl;
    });

    tp2.join();
    tp1.join();
}

Prints e.g.

trace tp2: thread 33
trace 2: thread 33
trace tp1: thread d6
trace 3: thread d6
trace 4: thread 33
trace 5: thread d6
trace 6: thread 33
trace 7: thread d6
done
sehe
  • 374,641
  • 47
  • 450
  • 633