boost-ext / sml

C++14 State Machine library
https://boost-ext.github.io/sml
Boost Software License 1.0
1.16k stars 179 forks source link

queue_event default move assignment operator could cause improper destruction #279

Open palarson opened 5 years ago

palarson commented 5 years ago

We've uncovered an issue when deferring events that hold shared pointers. The issue manifests when the event is on the back end of a defer queue deque and is erased. It turns out that the deque will move events on the queue backwards, and with the default move assignment operator, the move/dtor on the underlying data is not called properly.

The code below triggers the issue. The program prints

  1. 2
  2. 1

#include <queue>
#include <memory>
#include <boost/sml.hpp>

namespace sml = boost::sml;                                                     

struct e1 {
    std::shared_ptr<int> p;
};

struct e2 {};

struct e3 {};

struct table {
    auto operator()() noexcept {                                               
        using namespace sml;
        // clang-format off
        return make_transition_table(
            *"s1"_s + event<e1> / defer
            , "s1"_s + event<e2> / defer
            , "s1"_s + event<e3> = "s2"_s
            , "s2"_s + event<e1> / [](auto const& ev) { std::cout << *ev.p << ": " << ev.p.use_count() << std::endl; }
            , "s2"_s + event<e2> / defer
        );
        // clang-format on
    }
};

int main() {
    sml::sm<table, sml::defer_queue<std::deque>> sm;

    auto e1val = std::make_shared<int>(1);
    auto e2val = std::make_shared<int>(2);

    sm.process_event(e2{});
    sm.process_event(e2{});
    sm.process_event(e2{});
    sm.process_event(e2{});
    sm.process_event(e2{});
    sm.process_event(e2{});
    sm.process_event(e2{});    
    sm.process_event(e1{ e1val });
    sm.process_event(e1{ e2val });
    sm.process_event(e3{});
}
lumpidu commented 4 years ago

Can this be closed, because https://github.com/boost-experimental/sml/pull/280 has been merged ?