jhu-cisst / cisst

JHU ERC CISST Library
http://github.com/jhu-cisst/cisst/wiki
Other
64 stars 47 forks source link

Problem with disconnecting services #59

Open arimozes opened 6 years ago

arimozes commented 6 years ago

When running cisst tasks within the visual studio unit testing framework, it is important to be able to startup and shutdown the system while still holding on to the same process. I see a hack in the cisst sample tests related to this that was added a number of years back: // the manager singleton needs to be cleaned up, adeguet1 std::cerr << "temporary hack " << CMN_LOG_DETAILS << std::endl; manager->RemoveComponent("LCM_MCC"); manager->RemoveComponent("MCS"); However, this is not the only problem I am facing. I am able to trigger a crash and/or an error on disconnect by playing with the timing. A "simple" testcase is below, which I can use to trigger a crash by inserting a delay, or trigger an error by inserting a delay in a different spot.


#include <chrono>
#include <thread>
#include <stdint.h>
#include <cisstMultiTask/mtsTaskContinuous.h>
#include <cisstMultiTask/mtsTaskPeriodic.h>
#include <cisstMultiTask/mtsInterfaceRequired.h>
#include <cisstMultiTask/mtsInterfaceProvided.h>

class Server : public mtsTaskPeriodic
{
    CMN_DECLARE_SERVICES(CMN_NO_DYNAMIC_CREATION, CMN_LOG_ALLOW_DEBUG);
public:
    Server();
    virtual ~Server();
    void Configure(const std::string & CMN_UNUSED(filename)) {};
    void Startup(void);
    void Run(void);
    void Cleanup(void);
    int32_t data_;
};
CMN_DECLARE_SERVICES_INSTANTIATION(Server);

class Client : public mtsTaskContinuous
{
    CMN_DECLARE_SERVICES(CMN_NO_DYNAMIC_CREATION, CMN_LOG_ALLOW_DEBUG);
public:
    Client();
    virtual ~Client();
    struct {
        mtsFunctionRead GetData;
    } _Task;
    void Configure(const std::string & CMN_UNUSED(filename)) {};
    void Startup(void);
    void Run(void);
    void Cleanup(void);
};
CMN_DECLARE_SERVICES_INSTANTIATION(Client);

CMN_IMPLEMENT_SERVICES_DERIVED(Server, mtsTaskPeriodic);
CMN_IMPLEMENT_SERVICES_DERIVED(Client, mtsTaskContinuous);

Server::Server()
    : mtsTaskPeriodic("server", 0.005, false)
{
    StateTable.AddData(data_, "stab");
    mtsInterfaceProvided *addI = this->AddInterfaceProvided("pi");
    addI->AddCommandReadState(StateTable, data_, "c1");
}
Server::~Server()
{
}
void Server::Startup()
{
    data_ = 0;
}
void Server::Cleanup()
{
}
void Server::Run()
{
    ProcessQueuedCommands();
    ProcessQueuedEvents();
    data_++;
    Sleep(0.002);
}

Client::Client()
    : mtsTaskContinuous("client")
{
    mtsInterfaceRequired *addI = AddInterfaceRequired("ri");
    addI->AddFunction("c1", _Task.GetData);
}
Client::~Client()
{
}
void Client::Startup()
{
}
void Client::Cleanup()
{
}
void Client::Run()
{
    ProcessQueuedCommands();
    ProcessQueuedEvents();
    Sleep(0.002);
}

int  main(int argc, char **argv)
{
    std::cout << "starting" << std::endl;
    cmnLogger::SetMaskDefaultLog(CMN_LOG_ALLOW_ALL);

    Server *svr = new Server();
    Client *cli = new Client();

    mtsManagerLocal *taskManager = mtsManagerLocal::GetInstance();

    std::chrono::milliseconds dur(2000);

    taskManager->AddComponent(svr);
    taskManager->AddComponent(cli);

    taskManager->Connect(cli->GetName(), "ri", svr->GetName(), "pi");
    cli->_Task.GetData.IsValid();

    taskManager->CreateAll();
    taskManager->WaitForStateAll(mtsComponentState::READY, 2.0 * cmn_s);

    taskManager->StartAll();
    taskManager->WaitForStateAll(mtsComponentState::ACTIVE, 4.0 * cmn_s);

    // If this delay is removed, the first GetData returns garbage
    //std::this_thread::sleep_for(std::chrono::milliseconds(10));

    int32_t data = -1;
    std::cout << "data " << std::to_string(data) << std::endl;
    cli->_Task.GetData(data);
    std::cout << "data " << std::to_string(data) << std::endl;
    std::this_thread::sleep_for(dur);
    cli->_Task.GetData(data);
    std::cout << "data " << std::to_string(data) << std::endl;
    std::this_thread::sleep_for(dur);
    cli->_Task.GetData(data);
    std::cout << "data " << std::to_string(data) << std::endl;

    taskManager->KillAll();
    taskManager->WaitForStateAll(mtsComponentState::FINISHED, 2.0 * cmn_s);

    std::cout << "disconnecting" << std::endl;
    taskManager->Disconnect(cli->GetName(), "ri", svr->GetName(), "pi");
    std::cout << "finished disconnect call" << std::endl;

    // If this delay is added a crash happens within mtsManagerComponentClient::DisconnectLocally (when ultimately calling mtsMailBox::Write)
    std::this_thread::sleep_for(dur);
    std::cout << "finished sleep after disconnect" << std::endl;

    taskManager->RemoveComponent(cli);
    taskManager->RemoveComponent(svr);
    // If this delay is added (and the previous one removed to avoid the crash), cisst errors are logged regarding disconnect issues
    std::this_thread::sleep_for(dur);
    std::cout << "finished sleep after remove components" << std::endl;

    delete cli;
    delete svr;
    std::cout << "exiting" << std::endl;
}