Boost :: asio :: deadline_timer :: async_wait callback does not fire

I have a boost io_service

running on a thread and I would like to trigger a callback on that thread 6 seconds after some event happens to the client, and reset the timer for that client if it is already running.

I maintain unordered_map<string, shared_ptr<deadline_timer>>

with a timer for each client.

However, on setup, async_wait

my callback doesn't fire after the allotted time ( io_service

IS works ), and it does n't fire (with an error code) when I reset the pointer (which should call the destructor for the existing timer, forcing it to publish to the service). How can I fix this?

This is an important part of my code:

auto it = timersByClientId.find(clientId);
if (it == timersByClientId.end())
{
    onLogonChangeCallback(clientId, true);
    timersByClientId[clientId].reset(
        new boost::asio::deadline_timer(replyService, boost::posix_time::seconds(6))
    );
    it = timersByClientId.find(clientId);
}
else
{
    // Cancel current wait operation (should fire the callback with an error code)
    it->second.reset(
        new boost::asio::deadline_timer(replyService, boost::posix_time::seconds(6))
    );
}
it->second->async_wait([this, clientId](const boost::system::error_code& err) {
    if (!err)
    {
        onLogonChangeCallback(clientId, false);
    }
});

      

If it changes anything, I run under Visual C ++ 2010 and increase 1.47.0.

+2


source to share


1 answer


Your code / looks / good.

I'm not sure how you arrive at the conclusion that your completion handler is not "[...] fire (with an error code) when I reset the pointer". You are ignoring this case (there is no branch in the lambda else

).

What is the best way to write logic?

void foo(int clientId) {
    shared_timer& timer = timersByClientId[clientId];

    if (!timer) 
        onLogonChangeCallback(clientId, true);

    timer = make_timer(); // reset

    timer->async_wait([this, clientId](const boost::system::error_code& err) {
        if (!err)
            onLogonChangeCallback(clientId, false);
    });
}

      

Here's a complete demo with this thread else

so you can see what's going on. I have accepted 1 service thread.

Watch Live On Coliru .



The test load is 100 session operations on 16 accounts in ~ 0.5 s. The total running time is ~ 1.5s because I cut the end time from 6s to 1s for Coliru.

If you don't want the LogonManager destructor to wait for all sessions to finish, clear the sessions table before attaching to the background thread:

~LogonMonitor() {
    work = boost::none;
    timersByClientId.clear();
    background.join();
}

      

Full list

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/optional.hpp>
#include <boost/make_shared.hpp>

struct LogonMonitor {
    LogonMonitor() 
        : work(io_service::work(replyService)), background([this]{ replyService.run(); })
    { }

    ~LogonMonitor() {
        work = boost::none;
        // timersByClientId.clear();
        background.join();
    }

    void foo(int clientId) {
        shared_timer& timer = timersByClientId[clientId];

        if (!timer) 
            onLogonChangeCallback(clientId, true);

        timer = make_timer(); // reset

        timer->async_wait([this, clientId](const boost::system::error_code& err) {
            if (!err)
                onLogonChangeCallback(clientId, false);
            else
                std::cout << "(cancel " << clientId << " timer)" << std::endl;
        });
    }

  private:
    using io_service   = boost::asio::io_service;
    using timer        = boost::asio::deadline_timer;
    using shared_timer = boost::shared_ptr<timer>;

    io_service replyService;
    boost::optional<io_service::work> work;
    boost::thread background;

    std::map<int, shared_timer> timersByClientId;

    shared_timer make_timer() { 
        return boost::make_shared<timer>(replyService, boost::posix_time::seconds(/*6*/1)); 
    }

    void onLogonChangeCallback(int clientId, bool newLogon) 
    {
        std::cout << __FUNCTION__ << "(" << clientId << ", " << newLogon << ")" << std::endl;
    }
};

int main()
{
    LogonMonitor instance;
    for (int i = 0; i < 100; ++i)
    {
        instance.foo(rand() % 16);
        boost::this_thread::sleep_for(boost::chrono::milliseconds(rand() % 10));
    }
}

      

+3


source







All Articles