c++boostdeadline-timer

boost deadline_timer compile issue


The following source will not compile using MSVC 12.

IMCThreadMngr.cpp

#include "IMCThreadMngr.h"


CIMCThreadMngr::CIMCThreadMngr() : mService(),
mWork(mService)
{}

CIMCThreadMngr::~CIMCThreadMngr() {};


void CIMCThreadMngr::StopManager()
{
    std::cout << "Manager ceasing" << std::endl;
    mService.stop();
    mServicethread.join();
    std::cout << "Manager ceased" << std::endl;
}

void CIMCThreadMngr::StartManager()
{
    mServicethread = boost::thread(boost::bind(&boost::asio::io_service::run, &mService));
}

void CIMCThreadMngr::RegisterThread(const std::string& name, int timeout)
{
    if (name.length() == 0) {
        std::cout << "No thread name provided" << std::endl;
        return;
    }
    boost::mutex::scoped_lock lock(mGroupMutex);

    ThreadObject ob = ThreadObject(mService);   
    ob.name_ = name;
    if (timeout > 0) {
    ob.timeout_ = timeout;
    }
    else {
    ob.timeout_ = 2000;
    }
    mThreadGroup.push_back(ob);

}

void CIMCThreadMngr::UnRegisterThread(const std::string& name)
{
    if (name.length() == 0) {
        std::cout << "No thread name provided" << std::endl;
        return;
    }

    boost::mutex::scoped_lock lock(mGroupMutex);

    std::vector<ThreadObject>::iterator obref;
    if (FindThreadObject(name, obref)){
        mThreadGroup.erase(obref);
    }
}

void CIMCThreadMngr::ThreadCheckIn(const std::string& name){

    if (name.length() == 0) {
        std::cout << "No thread name provided" << std::endl;
        return;
    }

    boost::mutex::scoped_lock lock(mGroupMutex);

    std::vector<ThreadObject>::iterator obref;  
    if (FindThreadObject(name, obref)){
        obref->timer_.cancel();
        obref->timer_.expires_from_now(boost::posix_time::seconds(obref->timeout_));
        obref->timer_.async_wait(boost::bind(&CIMCThreadMngr::TimeoutElapsed, this));
    }
}

bool CIMCThreadMngr::FindThreadObject(const std::string name, std::vector<ThreadObject>::iterator& ob){

    for (ob = mThreadGroup.begin(); ob != mThreadGroup.end(); ob++) {
        if ((ob->name_.compare(name) == 0)) {
            return true;
        }
    }
    return false;
}

void CIMCThreadMngr::TimeoutElapsed(const boost::system::error_code& e, const std::string& name){

    boost::mutex::scoped_lock lock(mGroupMutex);

    if (e != boost::asio::error::operation_aborted)
    {
        std::cout << "Thread " << name << " did has not responded" << std::endl; // Timer was not cancelled, take necessary action.
        ThreadCheckIn(name);
    }
}

IMCThreadMngr.h

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/mutex.hpp>
#include <vector>


class CIMCThreadMngr {

public:

    struct ThreadObject {

        std::string name_;
        int timeout_;
        bool threadrunning_;
        boost::posix_time::ptime lastupdate_;
        boost::asio::deadline_timer timer_;

        ThreadObject(boost::asio::io_service& service) : timer_(service)
        {
            timer_.expires_from_now(boost::posix_time::millisec(3000));
        }

    };

public:
    CIMCThreadMngr();
    ~CIMCThreadMngr();

    void StopManager();
    void StartManager();

    void RegisterThread(const std::string& name, int timeout);
    void UnRegisterThread(const std::string& name);
    void ThreadCheckIn(const std::string& name);
    bool FindThreadObject(const std::string name, std::vector<ThreadObject>::iterator& ob);
    void TimeoutElapsed(const boost::system::error_code& e, const std::string& name);   
    void TimeoutElapsed( );

private:

    boost::asio::io_service mService;
    boost::asio::io_service::work mWork;
    boost::thread mServicethread;
    std::vector<ThreadObject> mThreadGroup;
    boost::mutex mGroupMutex;

};

The compiler issue I am running into is as follows

f:\boost\boost_1_57_0\boost\asio\basic_deadline_timer.hpp(510): error C2248: 'boost::asio::basic_io_object<TimerService,false>::operator =' : cannot access private member declared in class 'boost::asio::basic_io_object<TimerService,false>'
          with
          [
              TimerService=boost::asio::deadline_timer_service<boost::posix_time::ptime,boost::asio::time_traits<boost::posix_time::ptime>>
          ]
          f:\boost\boost_1_57_0\boost\asio\basic_io_object.hpp(164) : see declaration of 'boost::asio::basic_io_object<TimerService,false>::operator ='
          with
          [
              TimerService=boost::asio::deadline_timer_service<boost::posix_time::ptime,boost::asio::time_traits<boost::posix_time::ptime>>
          ]
          This diagnostic occurred in the compiler generated function 'boost::asio::basic_deadline_timer<boost::posix_time::ptime,boost::asio::time_traits<boost::posix_time::ptime>,boost::asio::deadline_timer_service<Time,TimeTraits>> &boost::asio::basic_deadline_timer<Time,TimeTraits,boost::asio::deadline_timer_service<Time,TimeTraits>>::operator =(const boost::asio::basic_deadline_timer<Time,TimeTraits,boost::asio::deadline_timer_service<Time,TimeTraits>> &)'
          with
         [
             Time=boost::posix_time::ptime,            TimeTraits=boost::asio::time_traits<boost::posix_time::ptime>
         ]

Hopefully the issue is something fairly straight forward, though I can't find an obvious difference between my code above, and the boost examples of deadline_timer.


Solution

  • boost::asio::deadline_timer is neither copyable nor moveable (nor copy-assignable nor move-assignable). As a result, neither is CIMCThreadMgr::ThreadObject, which means that you cannot have a std::vector<ThreadObject>.

    A simple way around this problem is to hold the deadline_timer in a shared_ptr, as in

    struct ThreadObject {
        std::string name_;
        int timeout_;
        bool threadrunning_;
        boost::posix_time::ptime lastupdate_;
    
        // HERE
        std::shared_ptr<boost::asio::deadline_timer> timer_;
    
        ThreadObject(boost::asio::io_service& service)
          // Also HERE
          : timer_(std::make_shared<boost::asio::deadline_timer>(service))
        {
            // -> instead of . now.
            timer_->expires_from_now(boost::posix_time::millisec(3000));
        }
    };
    

    If you go this route, you'll also have to replace . with -> where timer_ is being used:

    if (FindThreadObject(name, obref)){
        //           vv-- HERE
        obref->timer_->cancel();
        obref->timer_->expires_from_now(boost::posix_time::seconds(obref->timeout_));
        obref->timer_->async_wait(boost::bind(&CIMCThreadMngr::TimeoutElapsed, this));
    }
    

    It is probably possible to use a std::unique_ptr, but that may require larger changes to your code than replacing . with -> in a few places (because ThreadObject would only be moveable but not copyable).