chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.72k stars 1.19k forks source link

steady_timer.cancel() unable to release registered functions #1475

Open Shangwei1024 opened 1 month ago

Shangwei1024 commented 1 month ago
#include <iostream>
#include <memory>
#include <thread>

#include <boost/asio/steady_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>

// class bar;
class foo : public std::enable_shared_from_this<foo>
{
    public:
    foo() :
    cycle_timer_(io_),
    cycle_(2000)
    {

    }

    ~foo()
    {
        std::cout<<"foo destricted\n";
    }
    void on_message(const boost::system::error_code &_error){

        auto p = shared_from_this();
        start_timer();
        std::cout<<"on message\n";
        std::cout<< __func__ << p.use_count()<<std::endl;
    }

    void start_timer(){
        boost::system::error_code ec;
        cycle_timer_.expires_from_now(cycle_, ec);
        cycle_timer_.async_wait(
                std::bind(&foo::on_message, shared_from_this(),
                        std::placeholders::_1));
    }

    void start()
    {
        io_.run();
    }

    void stop(){
        boost::system::error_code ec;
        // cycle_timer_.~basic_waitable_timer();
        cycle_timer_.get_executor();
        int ret = cycle_timer_.cancel(ec);
        std::cout<<"cancel num is "<<ret<<std::endl;
        io_.stop();
    }

    private:
    boost::asio::io_service io_;
    boost::asio::steady_timer cycle_timer_;
    std::chrono::milliseconds cycle_;

};

int main(){

    std::shared_ptr<foo> f = std::make_shared<foo>();

    std::cout<< __func__ << f.use_count()<<std::endl;
    f->start_timer();

    std::shared_ptr<std::thread> th = std::make_shared<std::thread>(
        [f]{
            sleep(9);
            f->stop();
        });
    f->start();
    std::cout<< __func__ <<" stop "<< f.use_count()<<std::endl;

    return 1;
}

boost version is 1.72 I don`t understand why the finally f.use_count() is 2. I guess that the registered function for async_wait includes std::shared_ptr.

MatthiasKleesSchulz commented 1 month ago

When calling cycletimer.cancel(ec) your completion handler (on_message) will be called with an error. Since you dont handle this error, you start the timer again which queues the completion handler again with its own copy of f.

When handling the error, f.use_count() is 1.

void on_message(const boost::system::error_code &_error) {
    if (_error) return;

    auto p = shared_from_this();
    start_timer();
    std::cout << __func__ << " " << p.use_count() << std::endl;
}