I'd like to be able to do an asynchronous wait on a specific event. There are a lot of similar questions and answers on here (and all compile and work for me) but none with my specific scenario. Basically, what I need to be able to do is an async_wait, passing a yield context as the handler, to a timer that waits indefinitely, and is then canceled by another thread.
For example, there is this question which does something very similar, but instead of using a yield context, it uses a separate, standalone handler. There is also something like this question which uses a yield context, but waits for a specified amount of time.
I can change my code to look like either of the two examples above and things work fine. But for someone reason when I combine a yield_context handler and a cancelled timer, I get the following exception:
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::current_exception_std_exception_wrapper<std::runtime_error> >:
Program ended with exit code: 9
From what I can tell, it looks like things choke when trying to invoke the completion handler (which in this case is the yield context).
Alright, enough babbling, here's the code. I've tried to come up with as simple of an example as possible to illustrate it:
The class:
class Foo {
public:
Foo() : work_(io_service_), timer_(io_service_) {
thread_pool_.create_thread(boost::bind(&boost::asio::io_service::run, &io_service_));
timer_.expires_from_now(boost::posix_time::pos_infin);
}
~Foo() {
io_service_.stop();
thread_pool_.join_all();
}
void Wait(const boost::asio::yield_context& context) {
std::cout << "Waiting" << std::endl;
timer_.async_wait(context);
std::cout << "Done waiting" << std::endl;
}
void Notify() {
std::cout << "Notifying" << std::endl;
timer_.cancel();
}
void Write(int num) {
std::cout << "Sending buffer event" << std::endl;
Notify();
std::cout << "Sent buffer event" << std::endl;
}
void Read(const boost::asio::yield_context& context) {
std::cout << "Waiting on buffer event, size is " << buffer_.size() << std::endl;
Wait(context);
std::cout << "Received buffer event, size is now " << buffer_.size() << std::endl;
}
std::vector<int> buffer_;
boost::asio::io_service io_service_;
boost::thread_group thread_pool_;
boost::asio::io_service::work work_;
boost::asio::deadline_timer timer_;
};
Main:
boost::shared_ptr<Foo> foo(new Foo());
boost::asio::spawn(foo->io_service_, boost::bind(&Foo::Read, foo, _1));
boost::this_thread::sleep(boost::posix_time::seconds(2));
foo->Write(1);
boost::this_thread::sleep(boost::posix_time::seconds(4));
Output:
Waiting on buffer event
Waiting
Sending buffer event
Notifying
Sent buffer event
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::current_exception_std_exception_wrapper<std::runtime_error> >:
Now, if I change the wait method to a time that will time out before the cancel is called, everything is fine. I.e.:
void Wait(const boost::asio::yield_context& context) {
std::cout << "Waiting" << std::endl;
timer_.expires_from_now(boost::posix_time::seconds(1));
timer_.async_wait(context);
std::cout << "Done waiting" << std::endl;
}
Or, if I change wait to use a separate handler method, everything is fine. I.e.:
void Handler() {
std::cout << "Handler!" << std::endl;
}
void Wait(const boost::asio::yield_context& context) {
std::cout << "Waiting" << std::endl;
timer_.async_wait(boost::bind(&Foo::Handler, this));
std::cout << "Done waiting" << std::endl;
}
I'm assuming there must be something simpler I'm missing here: either this is impossible for some reason or I'm making some dumb mistake. Anyway, thanks in advance.