Hello, I've noticed that WaitHandler can be called after boost::asio::deadline_timer destruction. It may cause using of uninitialized data. Are there any other solutions of graceful destruction of boost::asio::deadline_timer to be sure that after this destruction WaitHandler will never be called? Example pseudo code: class UsingDeadlineTimer { char some_data_accessed_from_the_wait_handler[512]; boost::asio::deadline_timer timer; void WaitHandler(const boost::system::error_code &ec) { if(ec == boost::asio::error::operation_aborted) { return; } memset(some_data_accessed_from_the_wait_handler, 'E', 512); // cause the problem, because UsingDeadlineTimer may be already destroyed. } UsingDeadlineTimer() : timer(io_service) { timer.expires_at(the moment of UsingDeadlineTimer destruction); timer.async_wait( boost::bind(&UsingDeadlineTimer::WaitHandler, this, boost::asio::placeholders::error) ); } ~UsingDeadlineTimer() { timer.cancel(); // but we already can be in the WaitHandler body which will access some_data_accessed_from_the_wait_handler } }; Thank you, Daniel
I've noticed that WaitHandler can be called after boost::asio::deadline_timer destruction. It may cause using of uninitialized data. Are there any other solutions of graceful destruction of boost::asio::deadline_timer to be sure that after this destruction WaitHandler will never be called?
Yes, the solution is to use shared_from_this idiom.
Hello Igor, Thank you for quick response. But it requires of changing construction model. What if I want allocate objects at stack? May be there are some technique which doesn't require from user to use shared_ptr technique? For example why boost::asio::deadline_timer::~deadline_timer doesn't wait completion of WaitHandler? Or is it bad way to wait smth in a destructor? Thank you, Daniel
I've noticed that WaitHandler can be called after boost::asio::deadline_timer destruction. It may cause using of uninitialized data. Are there any other solutions of graceful destruction of boost::asio::deadline_timer to be sure that after this destruction WaitHandler will never be called?
Yes, the solution is to use shared_from_this idiom. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
I've noticed that WaitHandler can be called after boost::asio::deadline_timer destruction. It may cause using of uninitialized data. Are there any other solutions of graceful destruction of boost::asio::deadline_timer to be sure that after this destruction WaitHandler will never be called?
Yes, the solution is to use shared_from_this idiom.
<...>
But it requires of changing construction model. What if I want allocate objects at stack? May be there are some technique which doesn't require from user to use shared_ptr technique?
The bottom line is that the completion handler should outlive its async. operation. So you have 2 ways: 1) To create an "automatic linkage" between the operation and the handler, i.e. to cause the handler to be alive as long as the operation is in progress. Eg., you can pass the handler functor by value or by smart ptr. 2) To control the handler lifetime manually. No techniques here, just ad-hoc solution that matches your specific scenario. Of course the 1st approach is much more robust and preferable in most cases. (Note that it was not necessary to create the completion handler as a binder based on an object containing the i/o object that initiates the async. operation -- it was your deliberate choice... And by the way, it seems that you attempt to cancel the timer from a non-io-service thread, don't you? If yes, it's not thread-safe.)
For example why boost::asio::deadline_timer::~deadline_timer doesn't wait completion of WaitHandler?
It can't because it doesn't dispatch completion handlers. It's
io_service who is in charge of this.
But even if it could, it would be rather a problem than a solution.
Consider the following trivial program:
#include
Igor,
Why deadline_timer doesn't wait WaitHandler completion?
I don't understand why did you delete object from wait handler...
I meant a little bit different thing like this:
class graceful_deadline_timer : public boost::asio::deadline_timer
{
typedef boost::asio::deadline_timer super;
boost::function1
I've noticed that WaitHandler can be called after boost::asio::deadline_timer destruction. It may cause using of uninitialized data. Are there any other solutions of graceful destruction of boost::asio::deadline_timer to be sure that after this destruction WaitHandler will never be called?
Yes, the solution is to use shared_from_this idiom.
<...>
But it requires of changing construction model. What if I want allocate objects at stack? May be there are some technique which doesn't require from user to use shared_ptr technique?
The bottom line is that the completion handler should outlive its async. operation. So you have 2 ways: 1) To create an "automatic linkage" between the operation and the handler, i.e. to cause the handler to be alive as long as the operation is in progress. Eg., you can pass the handler functor by value or by smart ptr. 2) To control the handler lifetime manually. No techniques here, just ad-hoc solution that matches your specific scenario. Of course the 1st approach is much more robust and preferable in most cases. (Note that it was not necessary to create the completion handler as a binder based on an object containing the i/o object that initiates the async. operation -- it was your deliberate choice... And by the way, it seems that you attempt to cancel the timer from a non-io-service thread, don't you? If yes, it's not thread-safe.)
For example why boost::asio::deadline_timer::~deadline_timer doesn't wait completion of WaitHandler?
It can't because it doesn't dispatch completion handlers. It's io_service who is in charge of this. But even if it could, it would be rather a problem than a solution. Consider the following trivial program:
#include
#include #include using namespace boost; using namespace boost::asio; using namespace boost::posix_time;
void f(const system::error_code&, const deadline_timer *t) { delete t; // to wait or not to wait? }
int main() { io_service io; deadline_timer *t = new deadline_timer(io); t->expires_from_now(seconds(10)); t->async_wait(boost::bind(&f, _1, t)); io.run(); } _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
<...>
void f(const system::error_code&, const deadline_timer *t) { delete t; // to wait or not to wait? }
int main() { io_service io; deadline_timer *t = new deadline_timer(io); t->expires_from_now(seconds(10)); t->async_wait(boost::bind(&f, _1, t)); io.run(); }
I don't understand why did you delete object from wait handler...
Just to demonstrate a trivial use-case where your idea won't work.
I meant a little bit different thing like this: class graceful_deadline_timer : public boost::asio::deadline_timer { <...> void handler_wrapper(const boost::system::error_code &ec) { boost::lock_guardboost::recursive_mutex lock(wait_handler_lock); handler(ec); expired = true; wait_handler_done.notify_all(); } public: <...> ~graceful_deadline_timer() { boost::unique_lockboost::recursive_mutex lock(wait_handler_lock); if(!expired) { cancel(); while(!expired) { wait_handler_done.wait(lock); } } }
template <typename WaitHandler> void async_wait(WaitHandler _handler) { boost::lock_guardboost::recursive_mutex lock(wait_handler_lock); handler = _handler; expired = false; super::async_wait(boost::bind(&graceful_deadline_timer::handler_wrapper, this, boost::asio::placeholders::error)); } };
Ok, now please apply your code to the case I mentioned before. In particular, what the following code would do:
while(!expired) { wait_handler_done.wait(lock); }
Seems like a deadlock, doesn't it? (And again, all these locks in async_wait/d-tor/handler imply that you intend to access deadline_timer from multiple threads. Note that it's not safe as deadline_timer is not thread-safe: http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/reference/basic_dea...)
participants (2)
-
Daniel
-
Igor R