1

I am programming an UDP client. The protocol is written as a single response for a single request. I have a transaction class that sends the message and then runs a timer to wait for the response. If the response is received in expected time the timer is stopped and success callback is called. Otherwise the failure callback is called. Finally the finish callback is called unconditionally in both cases.

template <typename RequestType, typename ResponseType>
struct transaction{
    boost::asio::io_service&                _service;
    boost::asio::deadline_timer             _timer;
    typename ResponseType::buffer_type      _buff;
    RequestType                             _request;

    transaction(boost::asio::io_service& service, const RequestType& req): _service(service), _timer(service), _request(req){}
    virtual ~transaction(){}
    template <typename SuccessCallback, typename FailureCallback, typename FinishCallback>
    void exec(boost::asio::ip::udp::endpoint& endpoint, boost::asio::ip::udp::socket& socket, unsigned wait_seconds, SuccessCallback success, FailureCallback failure, FinishCallback finish){
        _timer.expires_from_now(boost::posix_time::seconds(wait_seconds));
        _timer.async_wait([this, &failure, &finish](const boost::system::error_code& error){
            if(!error){
                std::cout << "failed to receive reply within deadline" << std::endl;
                failure(_request);
                finish(this);
            }
        });
        socket.async_receive_from(boost::asio::buffer(_buff), endpoint, [this, &success, finish](const boost::system::error_code& error, std::size_t bytes_transferred){
            if(!error && bytes_transferred > 0){
                ResponseType reply;
                reply.parse(_buff);
                _timer.cancel();
                success(reply);
                finish(this);
            }
        });
        socket.send_to(boost::asio::buffer(_request.buffer()), endpoint);
    }
};

transaction is working fine for once. However I want to retry if it fails. Following is the knock function which is working fine for the first time. But in failure callback it calls knock again which results in segfault.

typedef transaction<request_knock, reply_knock> transaction_knock;
void knock(boost::asio::io_service& service, boost::asio::ip::udp::endpoint& endpoint, boost::asio::ip::udp::socket& socket){
    request_knock knock_request(0);
    transaction_knock* knock_transaction = new transaction_knock(service, knock_request);
    knock_transaction->exec(endpoint, socket, 5, [](const reply_knock& reply){
        std::cout << "success " << reply._data.id << std::endl;
    }, [&service, &endpoint, &socket](const request_knock& req){
        // FAILURE
        knock(service, endpoint, socket);
    }, [](transaction_knock* transaction){
        delete transaction;
    });
}

Following is the backtrace

#0  0x00007ffff7f0f244 in pthread_mutex_lock () from /usr/lib/libpthread.so.0
#1  0x000055555559f3c8 in boost::asio::detail::posix_mutex::lock (this=0xfbad2a8c) at /usr/include/boost/asio/detail/posix_mutex.hpp:52
#2  0x00005555555a7cb4 in boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>::scoped_lock (this=0x7fffffffe2a0, m=...)
    at /usr/include/boost/asio/detail/scoped_lock.hpp:46
#3  0x00005555555a02f5 in boost::asio::detail::service_registry::do_use_service (this=0xfbad2a84, key=..., 
    factory=0x5555555b4666 <boost::asio::detail::service_registry::create<boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >, boost::asio::io_context>(void*)>, owner=0x7ffff7bbe5c0 <_IO_2_1_stdout_>)
    at /usr/include/boost/asio/detail/impl/service_registry.ipp:117
#4  0x00005555555b39da in boost::asio::detail::service_registry::use_service<boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> > > (this=0xfbad2a84, owner=...) at /usr/include/boost/asio/detail/impl/service_registry.hpp:39
#5  0x00005555555b2219 in boost::asio::use_service<boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> > > (ioc=...) at /usr/include/boost/asio/impl/io_context.hpp:39
#6  0x00005555555b021d in boost::asio::basic_io_object<boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >, true>::basic_io_object (this=0x5555555e2f20, io_context=...) at /usr/include/boost/asio/basic_io_object.hpp:224
#7  0x00005555555ad795 in boost::asio::basic_deadline_timer<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> >::basic_deadline_timer (this=0x5555555e2f20, io_context=...) at /usr/include/boost/asio/basic_deadline_timer.hpp:159
#8  0x00005555555aa4ab in transaction<request_knock, reply_knock>::transaction (this=0x5555555e2f10, service=..., req=...)
    at /home/else/Projects/comet/main.cpp:111
#9  0x000055555559a90c in knock (service=..., endpoint=..., socket=...) at /home/else/Projects/comet/main.cpp:192
#10 0x000055555559a8a2 in <lambda(const request_knock&)>::operator()(const request_knock &) const (__closure=0x7fffffffe3d0, req=...)

I am trying to figure out what is cause for this crash ?

Neel Basu
  • 11,848
  • 10
  • 71
  • 138
  • To me, your knock object is deleted before *send_to* function ends. If handler passed into *async_receive_from* is called before *send_to* ends, finish(this) called from handler destroys knock which stores _request member which is used by *send_to*. – rafix07 Jan 17 '19 at 15:40
  • knock transaction object is deleted at finish callback which is called after getting e reply or time out. So the first knock object is living after the first send_to even after the first reply. That&#39;s why it&#39;s working once. The knock function always create a new transaction at the beginning so that must not depend on the deletion of the old transaction. Actually it is crashing when the knock transaction constructor creates a deadline timer for the 2nd time. – Neel Basu Jan 17 '19 at 15:45

0 Answers0