I have a problem with resuming boost::asio coroutine from another thread. Here is sample code:
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/spawn.hpp>
using namespace std;
using namespace boost;
void foo(asio::steady_timer& timer, asio::yield_context yield)
{
cout << "Enter foo" << endl;
timer.expires_from_now(asio::steady_timer::clock_type::duration::max());
timer.async_wait(yield);
cout << "Leave foo" << endl;
}
void bar(asio::steady_timer& timer)
{
cout << "Enter bar" << endl;
sleep(1); // wait a little for asio::io_service::run to be executed
timer.cancel();
cout << "Leave bar" << endl;
}
int main()
{
asio::io_service ioService;
asio::steady_timer timer(ioService);
asio::spawn(ioService, bind(foo, std::ref(timer), placeholders::_1));
thread t(bar, std::ref(timer));
ioService.run();
t.join();
return 0;
}
The problem is that the asio::steady_timer object is not thread safe and the program crashes. But if I try to use mutex to synchronize access to it then I have a deadlock because the scope of foo is not leaved.
#include <iostream>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/spawn.hpp>
using namespace std;
using namespace boost;
void foo(asio::steady_timer& timer, mutex& mtx, asio::yield_context yield)
{
cout << "Enter foo" << endl;
{
lock_guard<mutex> lock(mtx);
timer.expires_from_now(
asio::steady_timer::clock_type::duration::max());
timer.async_wait(yield);
}
cout << "Leave foo" << endl;
}
void bar(asio::steady_timer& timer, mutex& mtx)
{
cout << "Enter bar" << endl;
sleep(1); // wait a little for asio::io_service::run to be executed
{
lock_guard<mutex> lock(mtx);
timer.cancel();
}
cout << "Leave bar" << endl;
}
int main()
{
asio::io_service ioService;
asio::steady_timer timer(ioService);
mutex mtx;
asio::spawn(ioService, bind(foo, std::ref(timer), std::ref(mtx),
placeholders::_1));
thread t(bar, std::ref(timer), std::ref(mtx));
ioService.run();
t.join();
return 0;
}
There is no such a problem if I use standard completion handler instead of coroutines.
#include <iostream>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
using namespace std;
using namespace boost;
void baz(system::error_code ec)
{
cout << "Baz: " << ec.message() << endl;
}
void foo(asio::steady_timer& timer, mutex& mtx)
{
cout << "Enter foo" << endl;
{
lock_guard<mutex> lock(mtx);
timer.expires_from_now(
asio::steady_timer::clock_type::duration::max());
timer.async_wait(baz);
}
cout << "Leave foo" << endl;
}
void bar(asio::steady_timer& timer, mutex& mtx)
{
cout << "Enter bar" << endl;
sleep(1); // wait a little for asio::io_service::run to be executed
{
lock_guard<mutex> lock(mtx);
timer.cancel();
}
cout << "Leave bar" << endl;
}
int main()
{
asio::io_service ioService;
asio::steady_timer timer(ioService);
mutex mtx;
foo(std::ref(timer), std::ref(mtx));
thread t(bar, std::ref(timer), std::ref(mtx));
ioService.run();
t.join();
return 0;
}
Is it possible to have behavior similar to the last example when couroutines are used.
A coroutine runs within the context of a strand. In spawn(), if one is not explicitly provided, a new strand will be created for the coroutine. By explicitly providing strand to spawn(), one can post work into the strand that will be synchronized with the coroutine.
Also, as noted by sehe, undefined behavior may occur if the coroutine is running in one thread, acquires a mutex lock, then suspends, but resumes and runs in a different thread and releases the lock. To avoid this, ideally one should not hold locks while the coroutine suspends. However, if it is necessary, one must guarantee that the coroutine runs within the same thread when it is resumed, such as by only running the io_service from a single thread.
Here is the minimal complete example based on the original example where bar() posts work into a strand to cancel the timer, causing the foo() coroutine to resume:
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
void foo(boost::asio::steady_timer& timer, boost::asio::yield_context yield)
{
std::cout << "Enter foo" << std::endl;
timer.expires_from_now(
boost::asio::steady_timer::clock_type::duration::max());
boost::system::error_code error;
timer.async_wait(yield[error]);
std::cout << "foo error: " << error.message() << std::endl;
std::cout << "Leave foo" << std::endl;
}
void bar(
boost::asio::io_service::strand& strand,
boost::asio::steady_timer& timer
)
{
std::cout << "Enter bar" << std::endl;
// Wait a little for asio::io_service::run to be executed
std::this_thread::sleep_for(std::chrono::seconds(1));
// Post timer cancellation into the strand.
strand.post([&timer]()
{
timer.cancel();
});
std::cout << "Leave bar" << std::endl;
}
int main()
{
boost::asio::io_service io_service;
boost::asio::steady_timer timer(io_service);
boost::asio::io_service::strand strand(io_service);
// Use an explicit strand, rather than having the io_service create.
boost::asio::spawn(strand, std::bind(&foo,
std::ref(timer), std::placeholders::_1));
// Pass the same strand to the thread, so that the thread may post
// handlers synchronized with the foo coroutine.
std::thread t(&bar, std::ref(strand), std::ref(timer));
io_service.run();
t.join();
}
Which provides the following output:
Enter foo
Enter bar
foo error: Operation canceled
Leave foo
Leave bar
As covered in this answer, when the boost::asio::yield_context detects that the asynchronous operation has failed, such as when the operation is canceled, it converts the boost::system::error_code into a system_error exception and throws. The above example uses yield_context::operator[] to allow the yield_context to populate the provided error_code on failure instead of throwing throwing.
Related
I have a main program, this main program executes a thread that perform an action until the user triggers a stop. The problem that I have is if I add th.join() the main program won't continue until the thread finishes. And If there is no .join() the program crashs.
#include <iostream>
#include <thread>
#include <optional>
static bool s_finished = false;
using namespace std::literals::chrono_literals;
void SendData(int id)
{
std::cout << "Working thread: " << id << std::endl;
std::cout << "Started thread id: " << std::this_thread::get_id() << std::endl;
while (!s_finished)
{
std::cout << "Working\n";
std::this_thread::sleep_for(1s);
}
}
void startRecording(std::optional<int> t)
{
std::thread th1 (SendData, 1);
//th1.join();
std::cout << "[startRecording] Other Task" << std::endl;
}
void stopRecording()
{
s_finished = true;
std::cout << "[stopRecording] Other Task" << std::endl;
}
int main()
{
std::cout << "Start Program!" << std::endl;
startRecording();
std::this_thread::sleep_for(5s);
stopRecording();
return 0;
}
How can I do this?
Joining a thread will cause the program to stop until that thread is finished, and that's why the program blocks. We have to call join() eventually so that all child threads finish before the program exits, but we shouldn't call join until we need the child thread to be finished.
The simplest way to get the program to work is to return the thread from startRecording, so that we have control of it inside main. Then, we join the thread at the end of main, after we call stopRecording.
#include <iostream>
#include <thread>
#include <optional>
#include <atomic>
// (1) This needs to be atomic to avoid data races
std::atomic<bool> s_finished { false };
using namespace std::literals::chrono_literals;
void SendData(int id)
{
std::cout << "Working thread: " << id << std::endl;
std::cout << "Started thread id: " << std::this_thread::get_id() << std::endl;
while (!s_finished)
{
std::cout << "Working\n";
std::this_thread::sleep_for(1s);
}
}
std::thread startRecording(std::optional<int> t)
{
std::thread th1 (SendData, 1);
std::cout << "[startRecording] Other Task" << std::endl;
// (2) We return the thread so we can join it in main:
return th1;
}
void stopRecording()
{
s_finished = true;
std::cout << "[stopRecording] Other Task" << std::endl;
}
int main()
{
std::cout << "Start Program!" << std::endl;
// (3) We save the thread to a variable named 'worker'
// so we can join it later. I also added an input to startRecording b/c it needed one
std::thread worker = startRecording(std::optional<int>{1});
std::this_thread::sleep_for(5s);
stopRecording();
// (4) Join here, at the end
worker.join();
return 0;
}
Now, the program prints the expected output, then exits without problems:
Start Program!
[startRecording] Other Task
Working thread: 1
Started thread id: 139985258444544
Working
Working
Working
Working
Working
[stopRecording] Other Task
I marked my changes with (1), (2), (3), and (4) in the comments of the code. They're pretty small, and if you have questions about any of them I can provide additional explanation!
Addendum - using global variables when the signature of startRecording can't be changed
In general, it's best to avoid global variables, but I know it's not always possible to do so. if startRecording's signature can't be changed, we can't return a thread, so the thread has to be accessed globally. Here's how to do that:
#include <iostream>
#include <thread>
#include <optional>
#include <atomic>
// (1) This needs to be atomic to avoid data races
std::atomic<bool> s_finished { false };
// (2) we initialize this in startRecording
std::thread worker;
using namespace std::literals::chrono_literals;
void SendData(int id)
{
std::cout << "Working thread: " << id << std::endl;
std::cout << "Started thread id: " << std::this_thread::get_id() << std::endl;
while (!s_finished)
{
std::cout << "Working\n";
std::this_thread::sleep_for(1s);
}
}
void startRecording(std::optional<int> t)
{
// (3) worker gets initialized, and thread starts
worker = std::thread(SendData, 1);
std::cout << "[startRecording] Other Task" << std::endl;
}
void stopRecording()
{
s_finished = true;
std::cout << "[stopRecording] Other Task" << std::endl;
}
int main()
{
std::cout << "Start Program!" << std::endl;
startRecording(std::optional<int>{1});
std::this_thread::sleep_for(5s);
stopRecording();
// (4) Join here, at the end
worker.join();
return 0;
}
I use this code using async_read_some with timeout
readdata=0;
port_->async_read_some(boost::asio::buffer(vector),
boost::bind(readCallback));
//init async timer
boost::asio::deadline_timer timer(io);
timer.async_wait(boost::bind(timeoutHandler));
timer.expires_from_now(boost::posix_time::seconds(5));
io.reset();
do {
io.run_one();
}
while (readdata==0);
here are my callbacks
void readCallback()
{
std::cout << "READ CALLBACK: "<<x<<std::endl;
readdata=1;
return;
}
void timeoutHandler()
{
std::cout << "TIMEOUT CALLBACK: "<<x<<std::endl;
readdata=1;
}
my Problem is that timeoutHandler is is executed instantly and not after 5 seconds
Simple mistake. You should be doing expires_from_now before calling async_wait.
#include <iostream>
#include <asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
int main() {
asio::io_service io_s;
asio::deadline_timer timer(io_s);
timer.expires_from_now(boost::posix_time::seconds(5));
timer.async_wait([](auto err_c) { std::cout << "After 5 seconds" << std::endl; } );
io_s.reset();
io_s.run();
return 0;
}
I'm using a deadline_timer as an asynchronous event and I'm running into a situation where, after some time, the thread waiting on the event never seems to be woken up (despite more calls to cancel()). I've been able to reproduce this using some sample code that I've pasted below; it's not exactly consistent but I have seen what I think is the same issue I'm experiencing.
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_at(boost::posix_time::pos_infin);
int num_events = 0;
auto waiter = [&timer, &num_events](boost::asio::yield_context context) {
while (true) {
std::cout << "waiting on event" << std::endl;
boost::system::error_code e;
timer.async_wait(context[e]);
std::cout << "got event (" << e << ")" << std::endl;
++num_events;
}
};
boost::asio::spawn(io_service, std::move(waiter));
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
for (auto i = 0; i < 500000; ++i) {
timer.cancel();
std::cout << i << std::endl;
}
Am I doing something here that's unsupported and inadvertently hitting some race condition? The error code from the wait() never looks troublesome (even on the very last time it's woken up before it never seems to again). EDIT: I've also noticed the original bug on 3 different platforms (Windows, Mac and Linux) but the above test I've been using to reproduce has been on Windows.
The deadline_timer object is not threadsafe.
You're canceling it from another thread than the one that's posting the async_wait. This means the calls can race.
I'm not sure how this can completely inhibit the callback, in your sample. It seems to me that the program should /just/ quit because the tight loop to 500000 finishes quickly (doing many redundant cancels that never get processed, because the coroutine would e.g. not even have posted the new async_wait).
So maybe you mean, "why don't I get 500000 events".
UPDATE
After the comment, here's a trivial transformation that shows how you are gonna be fine calling members on the timer from within an actor. Note: this critically hinges on the idea that the io_service is run from a single thread only!
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_at(boost::posix_time::pos_infin);
boost::atomic_bool shutdown(false);
int num_events = 0;
auto waiter = [&timer, &num_events, &shutdown](boost::asio::yield_context context) {
while (!shutdown) {
std::cout << "waiting on event" << std::endl;
boost::system::error_code e;
timer.async_wait(context[e]);
std::cout << "got event (" << e.message() << ")" << std::endl;
++num_events;
}
};
boost::asio::spawn(io_service, std::move(waiter));
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
for (auto i = 0; i < 5000; ++i) {
io_service.post([&timer, i]{
std::cout << i << std::endl;
timer.cancel();
});
}
io_service.post([&]{
shutdown = true;
timer.cancel();
});
thread.join();
std::cout << "Check: " << num_events << " events counted\n";
}
Also, it looks like you just wanted to signal a background task. As given you can simplify the program like:
See it Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
io_service svc;
int num_events = 0;
auto work = boost::make_shared<io_service::work>(svc); // keep svc running
boost::thread thread(boost::bind(&io_service::run, &svc));
for (auto i = 0; i < 500000; ++i) {
svc.post([&num_events,i]{
std::cout << "got event (" << i << ")" << std::endl;
++num_events;
});
}
work.reset();
thread.join();
std::cout << "Check: " << num_events << " events counted\n";
}
This does print all 500000 events:
got event (0)
got event (1)
got event (3)
...
got event (499998)
got event (499999)
Check: 500000 events counted
I created two threads in my program. I wanted to terminate thread_1 inside thread_2 function and vice versa based on flag. I tried exit() and pthread_exit(Thread_id) but it does not work. I wanted to cancel the thread execution by calling pthread_cancel but the problem is that I cannot pass the thread id before pthread_create. Any suggestions ??
You can see how pthread_cancel works in the manpage.
However, since you mention C++, why not use the language features? Signaling one or many other threads can be done with condition-variables.
See it Live On Coliru
If you don't have C++11, you can use Boost Threads.
#include <thread>
#include <condition_variable>
#include <iostream>
using namespace std;
struct workers
{
mutex mx;
condition_variable cv;
bool canceled;
workers() : canceled(false) {}
void thread1()
{
cout << __PRETTY_FUNCTION__ << " start\n";
this_thread::sleep_for(chrono::seconds(2));
{
unique_lock<mutex> lk(mx);
cout << __PRETTY_FUNCTION__ << " signaling cancel\n";
canceled = true;
cv.notify_all();
}
this_thread::sleep_for(chrono::seconds(2));
cout << __PRETTY_FUNCTION__ << " done\n";
}
void thread2()
{
cout << __PRETTY_FUNCTION__ << " start\n";
for(;;)
{
// do some work
unique_lock<mutex> lk(mx);
if (cv.wait_for(lk, chrono::milliseconds(10), [this] { return canceled; }))
break;
}
cout << __PRETTY_FUNCTION__ << " done\n";
}
};
int main()
{
workers demo;
std::thread t1(&workers::thread1, ref(demo));
std::thread t2(&workers::thread2, ref(demo));
t1.join();
t2.join();
}
Output:
void workers::thread1() start
void workers::thread2() start
void workers::thread1() signaling cancel
void workers::thread2() done
void workers::thread1() done
Update The C++03 version with boost is Live On Coliru now too. I've added timestamps for fun:
thread1:21 2014-Mar-26 00:01:40.074269 start
thread2:37 2014-Mar-26 00:01:40.074275 start
thread1:26 2014-Mar-26 00:01:42.074873 signaling cancel
thread2:47 2014-Mar-26 00:01:42.074991 done
thread1:32 2014-Mar-26 00:01:44.075062 done
I have the following code snippet:
#include <signal.h>
#include <boost/asio.hpp>
#include <iostream>
void startAsyncWaitForSignal(boost::asio::io_service& ioService)
{
boost::asio::signal_set signals{ioService};
signals.add(SIGTERM);
signals.async_wait(
[&ioService](boost::system::error_code errorCode, int signalNumber)
{
std::cerr << errorCode.message() << std::endl;
if (!errorCode) {
std::cerr << "received signal " << signalNumber << std::endl;
startAsyncWaitForSignal(ioService);
}
}
);
}
int main() {
boost::asio::io_service ioService;
startAsyncWaitForSignal(ioService);
ioService.run();
}
I'd expect this program to wait, until the first SIGTERM is arrived, then wait to the next, then again to the next, ...
However the program is immediately terminates with the following output:
Operation canceled
What is the reason of this immediate operation cancel? I tried to make an io_service::work object, but that just changed the fact that ioService.run() was not finished, but the signal_set was still canceled immediately.
I am using boost 1.54. There is a bug fix related to asio/signals in 1.55, but that looks like a different issue.
When leaving startAsyncWaitForSignal() the local signal_set variable gets destroyed and the async_wait() call gets cancelled. The signal_set needs to live just a little longer. Move it out of startAsyncWaitForSignal() and pass it as a parameter, for example:
#include <signal.h>
#include <boost/asio.hpp>
#include <iostream>
void startAsyncWaitForSignal(boost::asio::io_service& ioService, boost::asio::signal_set& signals)
{
signals.async_wait(
[&ioService, &signals](boost::system::error_code errorCode, int signalNumber)
{
std::cerr << errorCode.message() << std::endl;
if (!errorCode) {
std::cerr << "received signal " << signalNumber << std::endl;
startAsyncWaitForSignal(ioService, signals);
}
}
);
}
int main() {
boost::asio::io_service ioService;
boost::asio::signal_set signals{ioService};
signals.add(SIGTERM);
startAsyncWaitForSignal(ioService, signals);
ioService.run();
}