Skip to content

Problem with disconnecting services #59

Open
@arimozes

Description

@arimozes

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;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions