boostorg / boostorg/boost

Boost ASIO multithreaded program which shares a single IO context hangs

Open
#798 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
HTML
Stars
8.6k
Forks
1.9k
Avg merge
39m
Merged PRs (30d)
2

Description

I am using boost version 1.79 on embedded linux 5.15 platform which is running on Dual Core Cortex A7 hardware.
I have the below code which I am testing to check sharing same IO context with multiple threads (which execute io.run)

```
#include
#include
#include

namespace asio = boost::asio;
using namespace std::chrono_literals;
using boost::system::error_code;

static std::atomic_int tid_gen = 0;
thread_local int const tid = [] { return ++tid_gen; }();
static constexpr auto now = std::chrono::steady_clock::now;
static auto const start = now();
static std::mutex console_mx;

void trace(auto const&... msg) {
std::lock_guard lk(console_mx);
std::cerr << "at " << std::setw(8) << (now() - start)/1ms << "ms - tid:" << tid << " ";
(std::cerr << ... << msg) << std::endl;
}

void worker(asio::io_context& ioContext) {
trace("Worker thread enter");
ioContext.run(); // Run the io_context to handle asynchronous operations
trace("Worker thread exit");
}

int main() {
try {
asio::io_context ioContext;

asio::steady_timer task1(ioContext, 100ms);
asio::steady_timer task2(ioContext, 200ms);

task1.async_wait([](error_code ec) {
trace("Start Task1: ", ec.message());
if (!ec)
sleep(6);
});

task2.async_wait([](error_code ec) {
trace("Start Task2: ", ec.message());
if (!ec)
sleep(12);
});

// Create a work object to prevent ioContext.run() from returning immediately
auto work = make_work_guard(ioContext);

// Create multiple worker threads
std::vector threads;
for (int i = 0; i < 2; ++i) {
threads.emplace_back(worker, std::ref(ioContext));
}

trace("App started :", std::thread::hardware_concurrency());

work.reset();
// Join the worker threads
for (auto& thread : threads) {
thread.join();
}

trace("All worker threads joined.");
} catch (std::exception const& e) {
trace("Exception: ", std::quoted(e.what()));
}
}
```
Output

```
root@hgx:~# /usr/bin/progress-code
at 0ms - tid:1 Worker thread enter
at 1ms - tid:2 App started :2
at 2ms - tid:3 Worker thread enter
at 100ms - tid:1 Start Task1: Success
at 6100ms - tid:1 Start Task2: Success
at 18100ms - tid:1 Worker thread exit

```
The program never terminates and need to be killed. One worker thread seem to hang and only one worker thread is picking up both timer callbacks serially.
I am not able to understand the behavior. As per this [article](https://theboostcpplibraries.com/boost.asio-scalability) both the threads should run concurrently and pickup the handlers.
Why one worker thread executing ioContext.run() always hangs in the above example. Need your help to understand if its a bug in the library or something wrong in the test code.

GDB back trace of the thread that hangs

```
#0 0x76cf5ce4 in pause () from /lib/libc.so.6
#1 0x0040a0c0 in boost::asio::detail::null_event::do_wait () at /home/ropai/SDK_GROUP/sysroots/armv7ahf-vfpv4d16-openbmc-linux-gnueabi/usr/include/boost/asio/detail/impl/null_event.ipp:47
#2 boost::asio::detail::null_event::wait (this=) at /home/ropai/SDK_GROUP/sysroots/armv7ahf-vfpv4d16-openbmc-linux-gnueabi/usr/include/boost/asio/detail/null_event.hpp:82
#3 boost::asio::detail::conditionally_enabled_event::wait (this=, lock=...) at /home/ropai/SDK_GROUP/sysroots/armv7ahf-vfpv4d16-openbmc-linux-gnueabi/usr/include/boost/asio/detail/conditionally_enabled_event.hpp:97
#4 boost::asio::detail::scheduler::do_run_one (this=this@entry=0x42d128, lock=..., this_thread=..., ec=...) at /home/ropai/SDK_GROUP/sysroots/armv7ahf-vfpv4d16-openbmc-linux-gnueabi/usr/include/boost/asio/detail/impl/scheduler.ipp:501
#5 0x004056c8 in boost::asio::detail::scheduler::run (ec=..., this=0x42d128) at /home/ropai/SDK_GROUP/sysroots/armv7ahf-vfpv4d16-openbmc-linux-gnueabi/usr/include/boost/asio/detail/impl/scheduler.ipp:210
#6 boost::asio::io_context::run (this=) at /home/ropai/SDK_GROUP/sysroots/armv7ahf-vfpv4d16-openbmc-linux-gnueabi/usr/include/boost/asio/impl/io_context.ipp:63
#7 worker (ioContext=...) at ../progress_code_main.cpp:182
```

If I start both the timers at the same time without any delay then both the threads pick up the handlers and exit correctly.

changes
```
asio::steady_timer task1(ioContext, 1ms);
asio::steady_timer task2(ioContext, 1ms);
```

Output
```
at 4ms - tid:1 App started :2
at 18ms - tid:2 Worker thread enter
at 26ms - tid:2 Start Task1: Success
at 31ms - tid:3 Worker thread enter
at 32ms - tid:3 Start Task2: Success
at 12033ms - tid:3 Worker thread exit
at 12033ms - tid:2 Worker thread exit
at 12034ms - tid:1 All worker threads joined.
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.