I am working on a boost asio project, where I have a bunch of stackful coroutines (https://www.boost.org/doc/libs/1_82_0/doc/html/boost_asio/overview/composition/spawn.html), performing asynchronous operations, and I use a strand to synchronize access to critical sections like so:
io_context ioctx;
io_context::strand my_strand{ioctx};
void my_coroutine(yield_context yield) {
while (true) {
post(my_strand, yield);
// Critical section, where one coroutine is supposed to have access
post(ioctx, yield);
}
}
However, I began to suspect that mutual exclusion was not happening, and wrote a small test to test out my hypothesis:
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
using namespace boost::asio;
io_context ioctx;
io_context::strand my_strand{ioctx};
std::atomic_int cnt{0};
void my_coroutine(yield_context yield) {
while (true) {
post(my_strand, yield);
cnt++;
assert(cnt == 1);
assert(my_strand.running_in_this_thread());
cnt--;
post(ioctx, yield);
}
}
int main() {
spawn(ioctx, [&](yield_context yield) { my_coroutine(yield); });
spawn(ioctx, [&](yield_context yield) { my_coroutine(yield); });
for (int i = 0; i < 5; ++i) {
std::thread([] { ioctx.run(); }).detach();
}
getchar();
}
As I suspected, both assertions fail when the program runs. I am not sure where the issue is.
Most surprisingly, commenting out one of the spawn calls still causes the running_in_this_thread
assertion to fail!
I am using Boost v1.81.0 on linux.
The executor specified in
post
is just a fallback. The associated executor of the token/handler takes precedence. In your case, the associated executor is the coroutine's strand. Your attempt to switch executors does nothing. To force your choice of executor, you can make sure it is associated with the token:and