orocos-toolchain / rtt

Orocos Real-Time Toolkit
http://www.orocos.org
Other
72 stars 79 forks source link

Deadlock when the input event port is disconnected from the output when the connection is active #334

Open pdima opened 2 years ago

pdima commented 2 years ago

The deadlock is caused by the different order of mutex locks during inputPort->disconnect(outputPort); // input port mutex locked, output port mutex locked and outputPort->write(pkt); // output port mutex locked, input port mutex locked.

The bug can be reproduced in the toolchain-2.10 branch, example to reproduce the issue:

#include <gtest/gtest.h>
#include <thread>

#include <rtt/TaskContext.hpp>
#include <rtt/Activity.hpp>
#include <rtt/extras/SequentialActivity.hpp>
#include <rtt/OutputPort.hpp>
#include <rtt/InputPort.hpp>

namespace {
struct TestComponentWithOutputPort : public RTT::TaskContext
{
    TestComponentWithOutputPort(const std::string& name)
            : RTT::TaskContext(name)
    {
        addPort("output_port", port);
    }

    void updateHook()
    {
        port.write(42);
    }

    RTT::OutputPort<int> port;
};

struct TestComponentWithInputPort : public RTT::TaskContext
{
    TestComponentWithInputPort(const std::string& name)
            : RTT::TaskContext(name)
    {
        addEventPort("input_port", port, [&](RTT::base::PortInterface*) {
            port.read(value);
        });
    }

    void updateHook()
    {
    }

    RTT::InputPort<int> port;
    int value {0};
};
}

TEST(TestPortConnection, test_reconnect_ports)
{
//    std::thread t([&](){ // workaround for setActivity failed from the main thread
        TestComponentWithOutputPort task1("task1");
        task1.setActivity(new RTT::Activity(ORO_SCHED_OTHER,50, 0.001));

        TestComponentWithInputPort task2("task2");
        task2.setActivity(new RTT::extras::SequentialActivity());

        task1.configure();
        task2.configure();

        task1.start();
        task2.start();

        for (int i=0; i<10000; i++)
        {
            task2.port.connectTo(&task1.port);
//            task2.disconnect();  // deadlock
            task2.port.disconnect(&task1.port); // deadlock
//            task1.port.disconnect(&task2.port); // no deadlock
        }

        task2.stop();
        task1.stop();
//
//    });
//    t.join();
}