From b710d507bd577c7ca3e59c08d5a885d1abc8c2b6 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Sat, 15 Jul 2023 11:56:25 +0200 Subject: [PATCH 01/27] wip --- examples/flight/flight.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index 955966be..2a77b1c6 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -2,6 +2,7 @@ #include #include +#include #include "symmetri/parsers.h" #include "symmetri/symmetri.h" From 1799c39a2530bccf26d0d4338c9274f5fd84f7a3 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Sat, 22 Jul 2023 21:44:10 +0200 Subject: [PATCH 02/27] faster switching by allocating a new reducer queue --- symmetri/tests/test_symmetri.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/symmetri/tests/test_symmetri.cc b/symmetri/tests/test_symmetri.cc index fbc1cbb2..081903a0 100644 --- a/symmetri/tests/test_symmetri.cc +++ b/symmetri/tests/test_symmetri.cc @@ -103,12 +103,12 @@ TEST_CASE("Reuse an application with a new case_id.") { TEST_CASE("Can not reuse an active application with a new case_id.") { auto [net, store, priority, m0] = testNet(); const auto initial_id = "initial1"; - const auto new_id = "something_different1"; + // const auto new_id = "something_different1"; auto stp = std::make_shared(1); symmetri::PetriNet app(net, m0, {}, store, priority, initial_id, stp); stp->push([&]() mutable { // this should fail because we can not do this while everything is active. - REQUIRE(!app.reuseApplication(new_id)); + // REQUIRE(!app.reuseApplication(new_id)); }); auto [ev, res] = symmetri::fire(app); } From 6dd592f37711c37f3220449cf5f711136c22186a Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Sat, 22 Jul 2023 21:57:31 +0200 Subject: [PATCH 03/27] make mermaid part of parsers --- examples/flight/flight.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index 2a77b1c6..955966be 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -2,7 +2,6 @@ #include #include -#include #include "symmetri/parsers.h" #include "symmetri/symmetri.h" From 95e7e65cee9a870ae9a156a2c57480811d0e1b47 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Sun, 23 Jul 2023 18:52:00 +0200 Subject: [PATCH 04/27] recursive eventlogging --- symmetri/tests/test_symmetri.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/symmetri/tests/test_symmetri.cc b/symmetri/tests/test_symmetri.cc index 081903a0..fbc1cbb2 100644 --- a/symmetri/tests/test_symmetri.cc +++ b/symmetri/tests/test_symmetri.cc @@ -103,12 +103,12 @@ TEST_CASE("Reuse an application with a new case_id.") { TEST_CASE("Can not reuse an active application with a new case_id.") { auto [net, store, priority, m0] = testNet(); const auto initial_id = "initial1"; - // const auto new_id = "something_different1"; + const auto new_id = "something_different1"; auto stp = std::make_shared(1); symmetri::PetriNet app(net, m0, {}, store, priority, initial_id, stp); stp->push([&]() mutable { // this should fail because we can not do this while everything is active. - // REQUIRE(!app.reuseApplication(new_id)); + REQUIRE(!app.reuseApplication(new_id)); }); auto [ev, res] = symmetri::fire(app); } From 663ea99b1f282f31d0bacd0090f4490c35b1ea27 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 27 Jul 2023 23:21:33 +0200 Subject: [PATCH 05/27] wip adl --- examples/flight/flight.cc | 20 ++---- examples/hello_world/main.cc | 4 +- examples/model_benchmark/model_benchmark.cc | 2 +- symmetri/CMakeLists.txt | 2 +- symmetri/include/symmetri/polytransition.h | 22 +++--- symmetri/include/symmetri/symmetri.h | 60 +++++------------ symmetri/symmetri.cc | 75 +++++++++++---------- symmetri/tests/test_external_input.cc | 2 +- symmetri/tests/test_symmetri.cc | 18 ++--- 9 files changed, 86 insertions(+), 119 deletions(-) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index 955966be..d40291a2 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -7,6 +7,7 @@ #include "symmetri/symmetri.h" #include "transition.hpp" +using namespace symmetri; /** * @brief We want to use the Foo class with Symmetri; Foo has nice * functionalities such as Pause and Resume and it can also get @@ -16,34 +17,21 @@ * all - if nothing is defined, a default version is used. * */ -namespace symmetri { -template <> Result fire(const Foo &f) { return f.fire() ? Result{{}, State::Error} : Result{{}, State::Completed}; } -template <> Result cancel(const Foo &f) { f.cancel(); return {{}, State::UserExit}; } -template <> -bool isDirect(const Foo &) { - return false; -} +bool isDirect(const Foo &) { return false; } -template <> -void pause(const Foo &f) { - f.pause(); -} +void pause(const Foo &f) { f.pause(); } -template <> -void resume(const Foo &f) { - f.resume(); -} -} // namespace symmetri +void resume(const Foo &f) { f.resume(); } /** * @brief A simple printer for the eventlog diff --git a/examples/hello_world/main.cc b/examples/hello_world/main.cc index f5bc9bd9..dfd9f6bd 100644 --- a/examples/hello_world/main.cc +++ b/examples/hello_world/main.cc @@ -84,8 +84,8 @@ int main(int, char *argv[]) { // becomes false. }); - auto [el, result] = symmetri::fire(net); // This function blocks until either - // the net completes, deadlocks + auto [el, result] = fire(net); // This function blocks until either + // the net completes, deadlocks // or user requests exit (ctrl-c) running.store(false); // We set this to false so the thread that we launched // gets interrupted. diff --git a/examples/model_benchmark/model_benchmark.cc b/examples/model_benchmark/model_benchmark.cc index c21ed69d..1a35ba67 100644 --- a/examples/model_benchmark/model_benchmark.cc +++ b/examples/model_benchmark/model_benchmark.cc @@ -23,7 +23,7 @@ int main(int, char *argv[]) { symmetri::PetriNet bignet(net, m0, {}, store, {}, "pluto", pool); spdlog::info("start!"); const auto start_time = symmetri::Clock::now(); - auto [el, result] = symmetri::fire(bignet); // infinite loop + auto [el, result] = fire(bignet); // infinite loop const auto end_time = symmetri::Clock::now(); auto trans_count = el.size() / 2; auto delta_t = (double((end_time - start_time).count()) / 1e9); diff --git a/symmetri/CMakeLists.txt b/symmetri/CMakeLists.txt index 5c7ef66d..1dd0df0f 100644 --- a/symmetri/CMakeLists.txt +++ b/symmetri/CMakeLists.txt @@ -21,13 +21,13 @@ include_directories(include submodules) #lib add_library(${PROJECT_NAME} SHARED tasks.cpp - symmetri.cc model.cc model_utilities.cc pnml_parser.cc grml_parser.cc eventlog_parsers.cc types.cpp + symmetri.cc submodules/tinyxml2/tinyxml2.cpp ) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) diff --git a/symmetri/include/symmetri/polytransition.h b/symmetri/include/symmetri/polytransition.h index 4270b440..baa9af6d 100644 --- a/symmetri/include/symmetri/polytransition.h +++ b/symmetri/include/symmetri/polytransition.h @@ -3,7 +3,6 @@ #include #include "symmetri/types.h" -namespace symmetri { /** * @brief Checks if the transition-function can be invoked. @@ -27,8 +26,8 @@ bool isDirect(const T &) { * @return Result */ template -Result cancel(const T &) { - return {{}, State::UserExit}; +symmetri::Result cancel(const T &) { + return {{}, symmetri::State::UserExit}; } /** @@ -58,24 +57,29 @@ void resume(const T &) {} * possible eventlog of the transition. */ template -Result fire(const T &transition) { +symmetri::Result fire(const T &transition) { + return fire(transition); if constexpr (!std::is_invocable_v) { - return {{}, State::Completed}; - } else if constexpr (std::is_same_v) { + return {{}, symmetri::State::Completed}; + } else if constexpr (std::is_same_v) { return {{}, transition()}; - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { return transition(); } else { transition(); - return {{}, State::Completed}; + return {{}, symmetri::State::Completed}; } } template -Eventlog getLog(const T &) { +symmetri::Eventlog getLog(const T &) { return {}; } +namespace symmetri { + /** * @brief PolyTransition is a wrapper around any type that you want to tie to a * transition. Typically this is an invokable object, such as a function, that diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index 7b2a5df2..6cb8cf55 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -119,47 +120,16 @@ class PetriNet final { */ bool reuseApplication(const std::string &case_id); - /** - * @brief Fire for a PetriNet means that it executes the Petri net until it - * reaches a final marking, deadlocks or is preempted by a user. - * - * @return Result - */ - friend Result fire(const PetriNet &app); - - /** - * @brief cancel interrupts and stops the Petri net execution and - * calls cancel on all child transitions that are active. If transitions do - * not have a cancel functionality implemented, they will not be cancelled. - * Their reducers however will not be processed. - * - * @return Result - */ - friend Result cancel(const PetriNet &app); - - /** - * @brief pause interrupts and pauses the Petri net execution and - * calls pause on all child transitions that are active. The Petri net will - * still consume reducers produced by finished transitions but it will not - * queue new transitions for execution. This mostly happens when active - * transitions do not have a pause-functionality implemented. - * - * @param app - */ - friend void pause(const PetriNet &app); - - /** - * @brief resume breaks the pause and immediately will try to fire all - * possible transitions. It will also call resume on all active transitions. - * - */ - friend void resume(const PetriNet &app); + // friend symmetri::Result(::fire)(const symmetri::PetriNet &); + // friend symmetri::Result(::cancel)(const symmetri::PetriNet &); + // friend void(::pause)(const symmetri::PetriNet &); + // friend void(::resume)(const symmetri::PetriNet &); + // friend bool(::isDirect)(const symmetri::PetriNet &); + // friend symmetri::Eventlog(::getLog)(const symmetri::PetriNet &); - friend Eventlog getLog(const PetriNet &app); - - private: std::shared_ptr impl; ///< Pointer to the implementation, all ///< information is stored in Petri + private: std::function register_functor; ///< At PetriNet construction this function is ///< created. It can be used to assign a trigger to @@ -167,11 +137,11 @@ class PetriNet final { ///< transition without meeting the pre-conditions. }; -Result fire(const PetriNet &); -Result cancel(const PetriNet &); -void pause(const PetriNet &); -void resume(const PetriNet &); -bool isDirect(const PetriNet &); -Eventlog getLog(const PetriNet &); - } // namespace symmetri + +symmetri::Result fire(const symmetri::PetriNet &); +symmetri::Result cancel(const symmetri::PetriNet &); +void pause(const symmetri::PetriNet &); +void resume(const symmetri::PetriNet &); +bool isDirect(const symmetri::PetriNet &); +symmetri::Eventlog getLog(const symmetri::PetriNet &); diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 11551255..976f157b 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -263,10 +263,12 @@ bool PetriNet::reuseApplication(const std::string &new_case_id) { } } -Result fire(const PetriNet &app) { +} // namespace symmetri + +symmetri::Result fire(const symmetri::PetriNet &app) { if (app.impl == nullptr) { spdlog::error("Something went seriously wrong. Please send a bug report."); - return {{}, State::Error}; + return {{}, symmetri::State::Error}; } auto &petri = *app.impl; @@ -276,18 +278,18 @@ Result fire(const PetriNet &app) { "[{0}] is already active, it can not run it again before it is " "finished.", app.impl->case_id); - return {{}, State::Error}; + return {{}, symmetri::State::Error}; } // we are running! auto &active_reducers = petri.reducers[petri.reducer_selector]; - petri.thread_id_.store(getThreadId()); + petri.thread_id_.store(symmetri::getThreadId()); petri.early_exit.store(false); m.event_log.clear(); m.tokens_n = m.initial_tokens; bool waiting_for_resume = false; - Reducer f; + symmetri::Reducer f; // start! m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); // get a reducer. Immediately, or wait a bit @@ -297,7 +299,7 @@ Result fire(const PetriNet &app) { m = f(std::move(m)); } while (!petri.early_exit.load() && active_reducers->try_dequeue(f)); - if (MarkingReached(m.tokens_n, petri.final_marking) || + if (symmetri::MarkingReached(m.tokens_n, petri.final_marking) || petri.early_exit.load()) { // we're done break; @@ -321,7 +323,7 @@ Result fire(const PetriNet &app) { } // determine what was the reason we terminated. - State result; + symmetri::State result; if (petri.early_exit.load()) { { // populate that eventlog with child eventlog and possible cancelations. @@ -332,16 +334,16 @@ Result fire(const PetriNet &app) { } m.event_log.push_back({petri.case_id, m.net.transition[transition_index], state, - Clock::now()}); + symmetri::Clock::now()}); } - result = State::UserExit; + result = symmetri::State::UserExit; } - } else if (MarkingReached(m.tokens_n, petri.final_marking)) { - result = State::Completed; + } else if (symmetri::MarkingReached(m.tokens_n, petri.final_marking)) { + result = symmetri::State::Completed; } else if (m.active_transitions_n.empty()) { - result = State::Deadlock; + result = symmetri::State::Deadlock; } else { - result = State::Error; + result = symmetri::State::Error; } // empty reducers @@ -356,15 +358,15 @@ Result fire(const PetriNet &app) { return {m.event_log, result}; }; -Result cancel(const PetriNet &app) { +symmetri::Result cancel(const symmetri::PetriNet &app) { const auto maybe_thread_id = app.impl->thread_id_.load(); - if (maybe_thread_id && maybe_thread_id.value() != getThreadId() && + if (maybe_thread_id && maybe_thread_id.value() != symmetri::getThreadId() && !app.impl->early_exit.load()) { std::mutex m; std::condition_variable cv; bool ready = false; app.impl->reducers[app.impl->reducer_selector]->enqueue( - [=, &m, &cv, &ready](Model &&model) { + [=, &m, &cv, &ready](symmetri::Model &&model) { app.impl->early_exit.store(true); { std::lock_guard lk(m); @@ -376,31 +378,34 @@ Result cancel(const PetriNet &app) { std::unique_lock lk(m); cv.wait(lk, [&] { return ready; }); } else { - app.impl->reducers[app.impl->reducer_selector]->enqueue([=](Model &&model) { - app.impl->early_exit.store(true); - return std::ref(model); - }); + app.impl->reducers[app.impl->reducer_selector]->enqueue( + [=](symmetri::Model &&model) { + app.impl->early_exit.store(true); + return std::ref(model); + }); } - return {app.getEventLog(), State::UserExit}; + return {app.getEventLog(), symmetri::State::UserExit}; } -void pause(const PetriNet &app) { - app.impl->reducers[app.impl->reducer_selector]->enqueue([&](Model &&model) { - model.is_paused = true; - return std::ref(model); - }); +void pause(const symmetri::PetriNet &app) { + app.impl->reducers[app.impl->reducer_selector]->enqueue( + [&](symmetri::Model &&model) { + model.is_paused = true; + return std::ref(model); + }); }; -void resume(const PetriNet &app) { - app.impl->reducers[app.impl->reducer_selector]->enqueue([&](Model &&model) { - model.is_paused = false; - return std::ref(model); - }); +void resume(const symmetri::PetriNet &app) { + app.impl->reducers[app.impl->reducer_selector]->enqueue( + [&](symmetri::Model &&model) { + model.is_paused = false; + return std::ref(model); + }); }; -bool isDirect(const PetriNet &) { return false; }; - -Eventlog getLog(const PetriNet &app) { return app.getEventLog(); } +bool isDirect(const symmetri::PetriNet &) { return false; }; -} // namespace symmetri +symmetri::Eventlog getLog(const symmetri::PetriNet &app) { + return app.getEventLog(); +} diff --git a/symmetri/tests/test_external_input.cc b/symmetri/tests/test_external_input.cc index d051b765..148c8880 100644 --- a/symmetri/tests/test_external_input.cc +++ b/symmetri/tests/test_external_input.cc @@ -31,7 +31,7 @@ TEST_CASE("Test external input.") { }); // run the net - auto [ev, res] = symmetri::fire(app); + auto [ev, res] = fire(app); REQUIRE(res == State::Completed); REQUIRE(i_ran); diff --git a/symmetri/tests/test_symmetri.cc b/symmetri/tests/test_symmetri.cc index fbc1cbb2..3a02e766 100644 --- a/symmetri/tests/test_symmetri.cc +++ b/symmetri/tests/test_symmetri.cc @@ -26,7 +26,7 @@ TEST_CASE("Create a using the net constructor without end condition.") { symmetri::PetriNet app(net, m0, {}, store, priority, "test_net_without_end", stp); // we can run the net - auto [ev, res] = symmetri::fire(app); + auto [ev, res] = fire(app); // because there's no final marking, but the net is finite, it deadlocks. REQUIRE(res == State::Deadlock); @@ -41,7 +41,7 @@ TEST_CASE("Create a using the net constructor with end condition.") { symmetri::PetriNet app(net, m0, final_marking, store, priority, "test_net_with_end", stp); // we can run the net - auto [ev, res] = symmetri::fire(app); + auto [ev, res] = fire(app); // now there is an end condition. REQUIRE(res == State::Completed); @@ -59,7 +59,7 @@ TEST_CASE("Create a using pnml constructor.") { PriorityTable priority; symmetri::PetriNet app({pnml_file}, {}, store, priority, "fail", stp); // however, we can try running it, - auto [ev, res] = symmetri::fire(app); + auto [ev, res] = fire(app); // but the result is an error. REQUIRE(res == State::Error); @@ -75,7 +75,7 @@ TEST_CASE("Create a using pnml constructor.") { symmetri::PetriNet app({pnml_file}, final_marking, store, priority, "success", stp); // so we can run it, - auto [ev, res] = symmetri::fire(app); + auto [ev, res] = fire(app); // and the result is properly completed. REQUIRE(res == State::Completed); REQUIRE(!ev.empty()); @@ -91,7 +91,7 @@ TEST_CASE("Reuse an application with a new case_id.") { REQUIRE(!app.reuseApplication(initial_id)); REQUIRE(app.reuseApplication(new_id)); // fire a transition so that there's an entry in the eventlog - auto eventlog = symmetri::fire(app).first; + auto eventlog = fire(app).first; // double check that the eventlog is not empty REQUIRE(!eventlog.empty()); // the eventlog should have a different case id. @@ -110,7 +110,7 @@ TEST_CASE("Can not reuse an active application with a new case_id.") { // this should fail because we can not do this while everything is active. REQUIRE(!app.reuseApplication(new_id)); }); - auto [ev, res] = symmetri::fire(app); + auto [ev, res] = fire(app); } TEST_CASE("Test pause and resume") { @@ -124,16 +124,16 @@ TEST_CASE("Test pause and resume") { stp->push([&, app, t1 = app.registerTransitionCallback("t1")]() { const auto dt = std::chrono::milliseconds(5); std::this_thread::sleep_for(dt); - symmetri::pause(app); + pause(app); std::this_thread::sleep_for(dt); check1 = i.load(); std::this_thread::sleep_for(dt); check2 = i.load(); - symmetri::resume(app); + resume(app); std::this_thread::sleep_for(dt); t1(); }); - symmetri::fire(app); + fire(app); REQUIRE(check1 == check2); REQUIRE(i.load() > check2); REQUIRE(i.load() > check2 + 1); From 1b606bf721043f9497b85f19886a49da3beb824d Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Fri, 28 Jul 2023 00:26:37 +0200 Subject: [PATCH 06/27] wip 2 --- examples/flight/flight.cc | 13 +++-- examples/hello_world/main.cc | 4 +- examples/model_benchmark/model_benchmark.cc | 2 +- symmetri/include/symmetri/symmetri.h | 60 +++++++++++---------- symmetri/symmetri.cc | 19 ++++--- symmetri/tests/test_external_input.cc | 3 +- symmetri/tests/test_symmetri.cc | 18 +++---- 7 files changed, 59 insertions(+), 60 deletions(-) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index d40291a2..3a88e397 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -70,17 +70,16 @@ int main(int, char *argv[]) { // Here we create the first PetriNet based on composing pnml1 and pnml2 // using flat composition. The associated transitions are two instance of // the Foo-class. - symmetri::PetriNet subnet({pnml1, pnml2}, {{"P2", 1}}, - {{"T0", Foo("SubFoo")}, {"T1", Foo("SubBar")}}, {}, - "SubNet", pool); + PetriNet subnet({pnml1, pnml2}, {{"P2", 1}}, + {{"T0", Foo("SubFoo")}, {"T1", Foo("SubBar")}}, {}, "SubNet", + pool); // We create another PetriNet by flatly composing all three petri nets. // Again we have 2 Foo-transitions, and the first transition (T0) is the // subnet. This show how you can also nest PetriNets. - symmetri::PetriNet bignet( - {pnml1, pnml2, pnml3}, {{"P3", 5}}, - {{"T0", subnet}, {"T1", Foo("Bar")}, {"T2", Foo("Foo")}}, {}, "RootNet", - pool); + PetriNet bignet({pnml1, pnml2, pnml3}, {{"P3", 5}}, + {{"T0", subnet}, {"T1", Foo("Bar")}, {"T2", Foo("Foo")}}, {}, + "RootNet", pool); // a flag to check if we are running std::atomic running(true); diff --git a/examples/hello_world/main.cc b/examples/hello_world/main.cc index dfd9f6bd..1d78db31 100644 --- a/examples/hello_world/main.cc +++ b/examples/hello_world/main.cc @@ -55,8 +55,8 @@ int main(int, char *argv[]) { // store based on the petri net. You can specifiy a final marking, the amount // of threads it can use (maximum amount of stuff it can do in parallel) and a // name so the net is easy to identifiy in a log. - symmetri::PetriNet net({pnml_path_start, pnml_path_passive}, {}, store, {}, - "CASE_X", pool); + PetriNet net({pnml_path_start, pnml_path_passive}, {}, store, {}, "CASE_X", + pool); // We use a simple boolean flag to terminate the threads once the net // finishes. Without it, these threads would prevent the program from cleanly diff --git a/examples/model_benchmark/model_benchmark.cc b/examples/model_benchmark/model_benchmark.cc index 1a35ba67..2bbe6d75 100644 --- a/examples/model_benchmark/model_benchmark.cc +++ b/examples/model_benchmark/model_benchmark.cc @@ -20,7 +20,7 @@ int main(int, char *argv[]) { } } - symmetri::PetriNet bignet(net, m0, {}, store, {}, "pluto", pool); + PetriNet bignet(net, m0, {}, store, {}, "pluto", pool); spdlog::info("start!"); const auto start_time = symmetri::Clock::now(); auto [el, result] = fire(bignet); // infinite loop diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index 6cb8cf55..f2c7a971 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -26,11 +26,21 @@ using Store = std::unordered_map; */ struct Petri; +} // namespace symmetri + /** * @brief The PetriNet class is a class that can create, configure and * run a Petri net. * */ +class PetriNet; +symmetri::Result fire(const PetriNet &); +symmetri::Result cancel(const PetriNet &); +void pause(const PetriNet &); +void resume(const PetriNet &); +bool isDirect(const PetriNet &); +symmetri::Eventlog getLog(const PetriNet &); + class PetriNet final { public: /** @@ -44,9 +54,9 @@ class PetriNet final { * @param stp */ PetriNet(const std::set &path_to_pnml, - const Marking &final_marking, const Store &store, - const PriorityTable &priority, const std::string &case_id, - std::shared_ptr stp); + const symmetri::Marking &final_marking, const symmetri::Store &store, + const symmetri::PriorityTable &priority, const std::string &case_id, + std::shared_ptr stp); /** * @brief Construct a new PetriNet object from a set of paths to @@ -59,8 +69,9 @@ class PetriNet final { * @param stp */ PetriNet(const std::set &path_to_grml, - const Marking &final_marking, const Store &store, - const std::string &case_id, std::shared_ptr stp); + const symmetri::Marking &final_marking, const symmetri::Store &store, + const std::string &case_id, + std::shared_ptr stp); /** * @brief Construct a new PetriNet object from a net and initial marking @@ -73,9 +84,10 @@ class PetriNet final { * @param case_id * @param stp */ - PetriNet(const Net &net, const Marking &m0, const Marking &final_marking, - const Store &store, const PriorityTable &priority, - const std::string &case_id, std::shared_ptr stp); + PetriNet(const symmetri::Net &net, const symmetri::Marking &m0, + const symmetri::Marking &final_marking, const symmetri::Store &store, + const symmetri::PriorityTable &priority, const std::string &case_id, + std::shared_ptr stp); /** * @brief register transition gives a handle to manually force a transition to @@ -98,7 +110,7 @@ class PetriNet final { * * @return Eventlog */ - Eventlog getEventLog() const noexcept; + symmetri::Eventlog getEventLog() const noexcept; /** * @brief Get the State, represented by a vector of *active* transitions (who @@ -109,8 +121,8 @@ class PetriNet final { * * @return std::pair, std::vector> */ - std::pair, std::vector> getState() - const noexcept; + std::pair, std::vector> + getState() const noexcept; /** * @brief reuseApplication resets the application such that the same net can @@ -120,28 +132,20 @@ class PetriNet final { */ bool reuseApplication(const std::string &case_id); - // friend symmetri::Result(::fire)(const symmetri::PetriNet &); - // friend symmetri::Result(::cancel)(const symmetri::PetriNet &); - // friend void(::pause)(const symmetri::PetriNet &); - // friend void(::resume)(const symmetri::PetriNet &); - // friend bool(::isDirect)(const symmetri::PetriNet &); - // friend symmetri::Eventlog(::getLog)(const symmetri::PetriNet &); + friend symmetri::Result(::fire)(const PetriNet &); + friend symmetri::Result(::cancel)(const PetriNet &); + friend void(::pause)(const PetriNet &); + friend void(::resume)(const PetriNet &); + friend bool(::isDirect)(const PetriNet &); + friend symmetri::Eventlog(::getLog)(const PetriNet &); - std::shared_ptr impl; ///< Pointer to the implementation, all - ///< information is stored in Petri private: + std::shared_ptr + impl; ///< Pointer to the implementation, all + ///< information is stored in Petri std::function register_functor; ///< At PetriNet construction this function is ///< created. It can be used to assign a trigger to ///< transitions - allowing the user to invoke a ///< transition without meeting the pre-conditions. }; - -} // namespace symmetri - -symmetri::Result fire(const symmetri::PetriNet &); -symmetri::Result cancel(const symmetri::PetriNet &); -void pause(const symmetri::PetriNet &); -void resume(const symmetri::PetriNet &); -bool isDirect(const symmetri::PetriNet &); -symmetri::Eventlog getLog(const symmetri::PetriNet &); diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 976f157b..ce91d646 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -205,6 +205,9 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, }}; } +} // namespace symmetri +using namespace symmetri; + PetriNet::PetriNet(const std::set &files, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, @@ -263,9 +266,7 @@ bool PetriNet::reuseApplication(const std::string &new_case_id) { } } -} // namespace symmetri - -symmetri::Result fire(const symmetri::PetriNet &app) { +symmetri::Result fire(const PetriNet &app) { if (app.impl == nullptr) { spdlog::error("Something went seriously wrong. Please send a bug report."); return {{}, symmetri::State::Error}; @@ -358,7 +359,7 @@ symmetri::Result fire(const symmetri::PetriNet &app) { return {m.event_log, result}; }; -symmetri::Result cancel(const symmetri::PetriNet &app) { +symmetri::Result cancel(const PetriNet &app) { const auto maybe_thread_id = app.impl->thread_id_.load(); if (maybe_thread_id && maybe_thread_id.value() != symmetri::getThreadId() && !app.impl->early_exit.load()) { @@ -388,7 +389,7 @@ symmetri::Result cancel(const symmetri::PetriNet &app) { return {app.getEventLog(), symmetri::State::UserExit}; } -void pause(const symmetri::PetriNet &app) { +void pause(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( [&](symmetri::Model &&model) { model.is_paused = true; @@ -396,7 +397,7 @@ void pause(const symmetri::PetriNet &app) { }); }; -void resume(const symmetri::PetriNet &app) { +void resume(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( [&](symmetri::Model &&model) { model.is_paused = false; @@ -404,8 +405,6 @@ void resume(const symmetri::PetriNet &app) { }); }; -bool isDirect(const symmetri::PetriNet &) { return false; }; +bool isDirect(const PetriNet &) { return false; }; -symmetri::Eventlog getLog(const symmetri::PetriNet &app) { - return app.getEventLog(); -} +symmetri::Eventlog getLog(const PetriNet &app) { return app.getEventLog(); } diff --git a/symmetri/tests/test_external_input.cc b/symmetri/tests/test_external_input.cc index 148c8880..fe22e42c 100644 --- a/symmetri/tests/test_external_input.cc +++ b/symmetri/tests/test_external_input.cc @@ -20,8 +20,7 @@ TEST_CASE("Test external input.") { Marking final_m = {{"Pb", 2}}; auto stp = std::make_shared(3); - symmetri::PetriNet app(net, m0, final_m, store, {}, "test_net_ext_input", - stp); + PetriNet app(net, m0, final_m, store, {}, "test_net_ext_input", stp); // enqueue a trigger; stp->push([trigger = app.registerTransitionCallback("t0")]() { diff --git a/symmetri/tests/test_symmetri.cc b/symmetri/tests/test_symmetri.cc index 3a02e766..2611b155 100644 --- a/symmetri/tests/test_symmetri.cc +++ b/symmetri/tests/test_symmetri.cc @@ -23,8 +23,7 @@ TEST_CASE("Create a using the net constructor without end condition.") { auto [net, store, priority, m0] = testNet(); auto stp = std::make_shared(1); - symmetri::PetriNet app(net, m0, {}, store, priority, "test_net_without_end", - stp); + PetriNet app(net, m0, {}, store, priority, "test_net_without_end", stp); // we can run the net auto [ev, res] = fire(app); @@ -38,8 +37,8 @@ TEST_CASE("Create a using the net constructor with end condition.") { Marking final_marking({{"Pa", 0}, {"Pb", 2}, {"Pc", 0}, {"Pd", 2}}); auto [net, store, priority, m0] = testNet(); - symmetri::PetriNet app(net, m0, final_marking, store, priority, - "test_net_with_end", stp); + PetriNet app(net, m0, final_marking, store, priority, "test_net_with_end", + stp); // we can run the net auto [ev, res] = fire(app); @@ -57,7 +56,7 @@ TEST_CASE("Create a using pnml constructor.") { // This store is not appropriate for this net, Store store = {{"wrong_id", &t0}}; PriorityTable priority; - symmetri::PetriNet app({pnml_file}, {}, store, priority, "fail", stp); + PetriNet app({pnml_file}, {}, store, priority, "fail", stp); // however, we can try running it, auto [ev, res] = fire(app); @@ -72,8 +71,7 @@ TEST_CASE("Create a using pnml constructor.") { Store store = symmetri::Store{{"T0", &t0}}; PriorityTable priority; Marking final_marking({{"P1", 1}}); - symmetri::PetriNet app({pnml_file}, final_marking, store, priority, - "success", stp); + PetriNet app({pnml_file}, final_marking, store, priority, "success", stp); // so we can run it, auto [ev, res] = fire(app); // and the result is properly completed. @@ -87,7 +85,7 @@ TEST_CASE("Reuse an application with a new case_id.") { const auto initial_id = "initial0"; const auto new_id = "something_different0"; auto stp = std::make_shared(1); - symmetri::PetriNet app(net, m0, {}, store, priority, initial_id, stp); + PetriNet app(net, m0, {}, store, priority, initial_id, stp); REQUIRE(!app.reuseApplication(initial_id)); REQUIRE(app.reuseApplication(new_id)); // fire a transition so that there's an entry in the eventlog @@ -105,7 +103,7 @@ TEST_CASE("Can not reuse an active application with a new case_id.") { const auto initial_id = "initial1"; const auto new_id = "something_different1"; auto stp = std::make_shared(1); - symmetri::PetriNet app(net, m0, {}, store, priority, initial_id, stp); + PetriNet app(net, m0, {}, store, priority, initial_id, stp); stp->push([&]() mutable { // this should fail because we can not do this while everything is active. REQUIRE(!app.reuseApplication(new_id)); @@ -119,7 +117,7 @@ TEST_CASE("Test pause and resume") { Store store = {{"t0", [&] { i++; }}, {"t1", [] {}}}; Marking m0 = {{"Pa", 1}}; auto stp = std::make_shared(2); - symmetri::PetriNet app(net, m0, {{"Pb", 1}}, store, {}, "random_id", stp); + PetriNet app(net, m0, {{"Pb", 1}}, store, {}, "random_id", stp); int check1, check2; stp->push([&, app, t1 = app.registerTransitionCallback("t1")]() { const auto dt = std::chrono::milliseconds(5); From dd602bfe178a7c8a961b751615939a5d5bc7999b Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Mon, 31 Jul 2023 19:37:59 +0200 Subject: [PATCH 07/27] formalizing direct mutation --- examples/flight/flight.cc | 4 +--- examples/model_benchmark/model_benchmark.cc | 2 +- symmetri/include/symmetri/polytransition.h | 7 ++++--- symmetri/include/symmetri/symmetri.h | 2 -- symmetri/model.cc | 7 ++++++- symmetri/model.h | 3 +++ symmetri/tests/test_external_input.cc | 2 +- symmetri/tests/test_model.cc | 4 ++-- symmetri/tests/test_polytransition.cc | 2 ++ symmetri/tests/test_priorities.cc | 2 +- 10 files changed, 21 insertions(+), 14 deletions(-) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index 3a88e397..42b9eece 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -13,7 +13,7 @@ using namespace symmetri; * functionalities such as Pause and Resume and it can also get * preempted/cancelled. We need to define functions to let Symmetri use these * functionalities. It is as simple by creating specialized version of the - * fire/cancel/isDirect/pause/resume functions. One does not need to implement + * fire/cancel/pause/resume functions. One does not need to implement * all - if nothing is defined, a default version is used. * */ @@ -27,8 +27,6 @@ Result cancel(const Foo &f) { return {{}, State::UserExit}; } -bool isDirect(const Foo &) { return false; } - void pause(const Foo &f) { f.pause(); } void resume(const Foo &f) { f.resume(); } diff --git a/examples/model_benchmark/model_benchmark.cc b/examples/model_benchmark/model_benchmark.cc index 2bbe6d75..56a24949 100644 --- a/examples/model_benchmark/model_benchmark.cc +++ b/examples/model_benchmark/model_benchmark.cc @@ -16,7 +16,7 @@ int main(int, char *argv[]) { if (bolus) { store.insert({t, []() {}}); } else { - store.insert({t, nullptr}); + store.insert({t, DirectMutation{}}); } } diff --git a/symmetri/include/symmetri/polytransition.h b/symmetri/include/symmetri/polytransition.h index baa9af6d..0b566b23 100644 --- a/symmetri/include/symmetri/polytransition.h +++ b/symmetri/include/symmetri/polytransition.h @@ -4,6 +4,8 @@ #include "symmetri/types.h" +struct DirectMutation {}; + /** * @brief Checks if the transition-function can be invoked. * @@ -14,7 +16,7 @@ */ template bool isDirect(const T &) { - return !std::is_invocable_v; + return false; } /** @@ -58,8 +60,7 @@ void resume(const T &) {} */ template symmetri::Result fire(const T &transition) { - return fire(transition); - if constexpr (!std::is_invocable_v) { + if constexpr (std::is_same_v) { return {{}, symmetri::State::Completed}; } else if constexpr (std::is_same_v) { diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index f2c7a971..0c0963e2 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -38,7 +38,6 @@ symmetri::Result fire(const PetriNet &); symmetri::Result cancel(const PetriNet &); void pause(const PetriNet &); void resume(const PetriNet &); -bool isDirect(const PetriNet &); symmetri::Eventlog getLog(const PetriNet &); class PetriNet final { @@ -136,7 +135,6 @@ class PetriNet final { friend symmetri::Result(::cancel)(const PetriNet &); friend void(::pause)(const PetriNet &); friend void(::resume)(const PetriNet &); - friend bool(::isDirect)(const PetriNet &); friend symmetri::Eventlog(::getLog)(const PetriNet &); private: diff --git a/symmetri/model.cc b/symmetri/model.cc index b280ef3d..d841f6dc 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -1,7 +1,12 @@ #include "model.h" -namespace symmetri { +bool isDirect(const DirectMutation &) { return true; } + +symmetri::Result fire(const DirectMutation &) { + return {{}, symmetri::State::Completed}; +} +namespace symmetri { std::tuple, std::vector, std::vector> convert(const Net &_net, const Store &_store) { diff --git a/symmetri/model.h b/symmetri/model.h index 154ff504..e90326a7 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -10,6 +10,9 @@ #include "symmetri/tasks.h" #include "symmetri/types.h" +bool isDirect(const DirectMutation &); +symmetri::Result fire(const DirectMutation &); + namespace symmetri { using SmallVector = gch::small_vector; diff --git a/symmetri/tests/test_external_input.cc b/symmetri/tests/test_external_input.cc index fe22e42c..39228cae 100644 --- a/symmetri/tests/test_external_input.cc +++ b/symmetri/tests/test_external_input.cc @@ -11,7 +11,7 @@ void t() { i_ran.store(true); } TEST_CASE("Test external input.") { Net net = { {"t0", {{}, {"Pa"}}}, {"t1", {{"Pa"}, {"Pb"}}}, {"t2", {{"Pc"}, {"Pb"}}}}; - Store store = {{"t0", nullptr}, + Store store = {{"t0", DirectMutation{}}, {"t1", &t}, {"t2", []() { std::this_thread::sleep_for(std::chrono::milliseconds(15)); diff --git a/symmetri/tests/test_model.cc b/symmetri/tests/test_model.cc index cac024f9..ff6e7e78 100644 --- a/symmetri/tests/test_model.cc +++ b/symmetri/tests/test_model.cc @@ -117,7 +117,7 @@ TEST_CASE("Run until net dies with nullptr") { using namespace moodycamel; auto [net, store, priority, m0] = testNet(); - store = {{"t0", nullptr}, {"t1", nullptr}}; + store = {{"t0", DirectMutation{}}, {"t1", DirectMutation{}}}; Model m(net, store, priority, m0); auto reducers = std::make_shared>(4); @@ -150,7 +150,7 @@ TEST_CASE( Store store; for (auto [t, dm] : net) { - store.insert({t, nullptr}); + store.insert({t, DirectMutation{}}); } // with this initial marking, all but transition e are possible. Marking m0 = {{"Pa", 1}}; diff --git a/symmetri/tests/test_polytransition.cc b/symmetri/tests/test_polytransition.cc index 514cd284..6dd741c2 100644 --- a/symmetri/tests/test_polytransition.cc +++ b/symmetri/tests/test_polytransition.cc @@ -23,6 +23,8 @@ class Foo { const int copy_constructor; }; +Result fire(const Foo&) { return {{}, State::Completed}; } + void resume(const Foo& f) { REQUIRE(f.constructor == 1); REQUIRE(f.copy_constructor == 0); diff --git a/symmetri/tests/test_priorities.cc b/symmetri/tests/test_priorities.cc index f00c6569..c5e1ca48 100644 --- a/symmetri/tests/test_priorities.cc +++ b/symmetri/tests/test_priorities.cc @@ -49,7 +49,7 @@ TEST_CASE("Using nullptr does not queue reducers.") { for (auto priority : priorities) { auto reducers = std::make_shared>(4); Net net = {{"t0", {{"Pa"}, {"Pb"}}}, {"t1", {{"Pa"}, {"Pc"}}}}; - Store store = {{"t0", nullptr}, {"t1", nullptr}}; + Store store = {{"t0", DirectMutation{}}, {"t1", DirectMutation{}}}; Marking m0 = {{"Pa", 1}, {"Pb", 0}, {"Pc", 0}}; auto stp = std::make_shared(1); From 07a86dcea46136990674a5440e26cc9c233ec78b Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Mon, 31 Jul 2023 19:53:10 +0200 Subject: [PATCH 08/27] make getLog the only function --- examples/flight/flight.cc | 5 +- symmetri/include/symmetri/symmetri.h | 11 ---- symmetri/symmetri.cc | 75 ++++++++++++---------------- 3 files changed, 35 insertions(+), 56 deletions(-) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index 42b9eece..df7bd7b6 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -85,11 +85,10 @@ int main(int, char *argv[]) { // a thread that polls the eventlog and writes it to a file auto gantt = std::thread([&] { while (running) { - writeMermaidHtmlToFile( - symmetri::mermaidFromEventlog(bignet.getEventLog())); + writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(getLog(bignet))); std::this_thread::sleep_for(std::chrono::seconds(3)); } - writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(bignet.getEventLog())); + writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(getLog(bignet))); }); // Parallel to the PetriNet execution, we run a thread through which we diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index 0c0963e2..f8369b20 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -101,16 +100,6 @@ class PetriNet final { std::function registerTransitionCallback( const std::string &transition) const noexcept; - /** - * @brief Get the Event Log object. If the Petri net is running, this call is - * blocking as it is executed on the Petri net execution loop. Otherwise it - * directly returns the log. - * - * - * @return Eventlog - */ - symmetri::Eventlog getEventLog() const noexcept; - /** * @brief Get the State, represented by a vector of *active* transitions (who * can still produce reducers and hence marking mutations) and the *current diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index ce91d646..d12b4cf6 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -108,34 +108,6 @@ struct Petri { return reducers[reducer_selector]; } - /** - * @brief Get the Event Log object - * - * @return Eventlog - */ - Eventlog getEventLog() const noexcept { - const auto maybe_thread_id = thread_id_.load(); - if (maybe_thread_id && maybe_thread_id.value() != getThreadId()) { - std::promise el; - std::future el_getter = el.get_future(); - reducers[reducer_selector]->enqueue([&](Model &&model) { - auto eventlog = model.event_log; - // get event log from parent nets: - for (size_t pt_idx : model.active_transitions_n) { - auto sub_el = getLog(model.net.store[pt_idx]); - if (!sub_el.empty()) { - eventlog.insert(eventlog.end(), sub_el.begin(), sub_el.end()); - } - } - el.set_value(std::move(eventlog)); - return std::ref(model); - }); - return el_getter.get(); - } else { - return m.event_log; - } - } - std::pair, std::vector> getState() const noexcept { const auto maybe_thread_id = thread_id_.load(); @@ -240,8 +212,6 @@ PetriNet::PetriNet(const Net &net, const Marking &m0, } } -Eventlog PetriNet::getEventLog() const noexcept { return impl->getEventLog(); }; - std::pair, std::vector> PetriNet::getState() const noexcept { return impl->getState(); @@ -360,13 +330,14 @@ symmetri::Result fire(const PetriNet &app) { }; symmetri::Result cancel(const PetriNet &app) { - const auto maybe_thread_id = app.impl->thread_id_.load(); + const auto &impl = *app.impl; + const auto maybe_thread_id = impl.thread_id_.load(); if (maybe_thread_id && maybe_thread_id.value() != symmetri::getThreadId() && - !app.impl->early_exit.load()) { + !impl.early_exit.load()) { std::mutex m; std::condition_variable cv; bool ready = false; - app.impl->reducers[app.impl->reducer_selector]->enqueue( + impl.reducers[impl.reducer_selector]->enqueue( [=, &m, &cv, &ready](symmetri::Model &&model) { app.impl->early_exit.store(true); { @@ -379,14 +350,13 @@ symmetri::Result cancel(const PetriNet &app) { std::unique_lock lk(m); cv.wait(lk, [&] { return ready; }); } else { - app.impl->reducers[app.impl->reducer_selector]->enqueue( - [=](symmetri::Model &&model) { - app.impl->early_exit.store(true); - return std::ref(model); - }); + impl.reducers[impl.reducer_selector]->enqueue([=](symmetri::Model &&model) { + app.impl->early_exit.store(true); + return std::ref(model); + }); } - return {app.getEventLog(), symmetri::State::UserExit}; + return {getLog(app), symmetri::State::UserExit}; } void pause(const PetriNet &app) { @@ -405,6 +375,27 @@ void resume(const PetriNet &app) { }); }; -bool isDirect(const PetriNet &) { return false; }; - -symmetri::Eventlog getLog(const PetriNet &app) { return app.getEventLog(); } +symmetri::Eventlog getLog(const PetriNet &app) { + const auto &impl = *app.impl; + const auto maybe_thread_id = impl.thread_id_.load(); + if (maybe_thread_id && maybe_thread_id.value() != getThreadId()) { + std::promise el; + std::future el_getter = el.get_future(); + impl.reducers[impl.reducer_selector]->enqueue([&](Model &&model) { + auto eventlog = model.event_log; + // get event log from parent nets: + for (size_t pt_idx : model.active_transitions_n) { + auto sub_el = getLog(model.net.store[pt_idx]); + if (!sub_el.empty()) { + eventlog.insert(eventlog.end(), sub_el.begin(), sub_el.end()); + } + } + el.set_value(std::move(eventlog)); + return std::ref(model); + }); + return el_getter.get(); + } else { + return impl.m.event_log; + } + getLog(app); +} From c8b58dec1b530842c542b14d01692f705bd0846f Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Mon, 31 Jul 2023 20:06:29 +0200 Subject: [PATCH 09/27] rename some funcs --- symmetri/model.cc | 13 ++++--------- symmetri/model.h | 27 +++++++++++++-------------- symmetri/model_utilities.cc | 7 ++++--- symmetri/symmetri.cc | 24 +----------------------- 4 files changed, 22 insertions(+), 49 deletions(-) diff --git a/symmetri/model.cc b/symmetri/model.cc index d841f6dc..1f29d4cb 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -122,7 +122,7 @@ bool Model::Fire( const size_t t, const std::shared_ptr> &reducers, - std::shared_ptr pool, const std::string &case_id) { + const std::shared_ptr &pool, const std::string &case_id) { auto timestamp = Clock::now(); // deduct the marking for (const size_t place : net.input_n[t]) { @@ -146,8 +146,7 @@ bool Model::Fire( active_transitions_n.push_back(t); event_log.push_back({case_id, transition, State::Scheduled, timestamp}); pool->push([=] { - reducers->enqueue( - createReducerForTransition(t, transition, task, case_id, reducers)); + reducers->enqueue(fireTransition(t, transition, task, case_id, reducers)); }); return false; } @@ -157,7 +156,7 @@ bool Model::fire( const Transition &t, const std::shared_ptr> &reducers, - std::shared_ptr pool, const std::string &case_id) { + const std::shared_ptr &pool, const std::string &case_id) { auto it = std::find(net.transition.begin(), net.transition.end(), t); return it != net.transition.end() && canFire(net.input_n[std::distance(net.transition.begin(), it)], @@ -169,7 +168,7 @@ bool Model::fire( void Model::fireTransitions( const std::shared_ptr> &reducers, - std::shared_ptr pool, bool run_all, + const std::shared_ptr &pool, bool run_all, const std::string &case_id) { // find possible transitions auto possible_transition_list_n = @@ -203,10 +202,6 @@ void Model::fireTransitions( return; } -std::pair, std::vector> Model::getState() const { - return {getActiveTransitions(), getMarking()}; -} - std::vector Model::getMarking() const { std::vector marking; marking.reserve(tokens_n.size()); diff --git a/symmetri/model.h b/symmetri/model.h index e90326a7..9cc378f7 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -66,7 +66,7 @@ using Reducer = std::function; * @param reducers * @return Reducer */ -Reducer createReducerForTransition( +Reducer fireTransition( size_t T_i, const std::string &transition, const PolyTransition &task, const std::string &case_id, const std::shared_ptr> @@ -98,6 +98,13 @@ struct Model { Model &operator=(Model const &) = default; Model &operator=(Model &&) noexcept = default; + /** + * @brief outputs the marking as a vector of tokens; e.g. [1 1 1 0 5] means 3 + * tokens in place 1, 1 token in place 0 and 1 token in place 5. + * + * @param marking + * @return std::vector + */ std::vector toTokens(const Marking &marking) const noexcept; /** @@ -129,14 +136,6 @@ struct Model { */ std::vector getFireableTransitions() const; - /** - * @brief Get the State; this is a pair of active transitions *and* the - * current marking. - * - * @return std::pair, std::vector> - */ - std::pair, std::vector> getState() const; - /** * @brief Try to fire a single transition. * @@ -151,7 +150,7 @@ struct Model { bool fire(const Transition &t, const std::shared_ptr> &reducers, - std::shared_ptr polymorphic_actions, + const std::shared_ptr &polymorphic_actions, const std::string &case_id = "undefined_case_id"); /** @@ -167,8 +166,8 @@ struct Model { void fireTransitions( const std::shared_ptr> &reducers, - std::shared_ptr polymorphic_actions, bool run_all = true, - const std::string &case_id = "undefined_case_id"); + const std::shared_ptr &polymorphic_actions, + bool run_all = true, const std::string &case_id = "undefined_case_id"); struct { std::vector @@ -202,7 +201,7 @@ struct Model { /** * @brief fires a transition. * - * @param t + * @param t transition as index in transition vector * @param reducers * @param polymorphic_actions * @param case_id @@ -212,7 +211,7 @@ struct Model { bool Fire(const size_t t, const std::shared_ptr> &reducers, - std::shared_ptr polymorphic_actions, + const std::shared_ptr &polymorphic_actions, const std::string &case_id); }; diff --git a/symmetri/model_utilities.cc b/symmetri/model_utilities.cc index d99236ff..6c308374 100644 --- a/symmetri/model_utilities.cc +++ b/symmetri/model_utilities.cc @@ -37,7 +37,8 @@ gch::small_vector possibleTransitions( return possible_transition_list_n; } -Reducer processTransition(size_t t_i, const Eventlog &ev, State result) { +Reducer createReducerForTransition(size_t t_i, const Eventlog &ev, + State result) { return [=](Model &&model) { // if it is in the active transition set it means it is finished and we // should process it. @@ -56,7 +57,7 @@ Reducer processTransition(size_t t_i, const Eventlog &ev, State result) { }; } -Reducer createReducerForTransition( +Reducer fireTransition( size_t t_i, const std::string &transition, const PolyTransition &task, const std::string &case_id, const std::shared_ptr> @@ -70,7 +71,7 @@ Reducer createReducerForTransition( auto [ev, res] = fire(task); ev.push_back({case_id, transition, res, Clock::now()}); - return processTransition(t_i, ev, res); + return createReducerForTransition(t_i, ev, res); } } // namespace symmetri diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index d12b4cf6..de411003 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -108,23 +108,6 @@ struct Petri { return reducers[reducer_selector]; } - std::pair, std::vector> getState() - const noexcept { - const auto maybe_thread_id = thread_id_.load(); - if (maybe_thread_id && maybe_thread_id.value() != getThreadId()) { - std::promise, std::vector>> - state; - auto getter = state.get_future(); - reducers[reducer_selector]->enqueue([&](Model &&model) { - state.set_value(model.getState()); - return std::ref(model); - }); - return getter.get(); - } else { - return m.getState(); - } - } - std::vector getFireableTransitions() const noexcept { const auto maybe_thread_id = thread_id_.load(); if (maybe_thread_id && maybe_thread_id.value() != getThreadId()) { @@ -168,7 +151,7 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, reducer->enqueue([=](Model &&m) { const auto t_index = toIndex(m.net.transition, t); m.active_transitions_n.push_back(t_index); - reducer->enqueue(createReducerForTransition( + reducer->enqueue(fireTransition( t_index, m.net.transition[t_index], m.net.store[t_index], impl->case_id, reducer)); return std::ref(m); @@ -212,11 +195,6 @@ PetriNet::PetriNet(const Net &net, const Marking &m0, } } -std::pair, std::vector> PetriNet::getState() - const noexcept { - return impl->getState(); -} - std::function PetriNet::registerTransitionCallback( const Transition &transition) const noexcept { return [transition, this] { register_functor(transition); }; From aca9cc455f932f263b045bc67a5cb087d2227f3d Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Mon, 31 Jul 2023 20:26:28 +0200 Subject: [PATCH 10/27] simplify --- symmetri/model.h | 2 +- symmetri/symmetri.cc | 33 +++++++++++++++------------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/symmetri/model.h b/symmetri/model.h index 9cc378f7..689137df 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -191,7 +191,7 @@ struct Model { ///< `transition` so it is compatible with index lookup. } net; ///< Is a data-oriented design of a Petri net - std::vector initial_tokens; ///< The intial marking + std::vector initial_tokens; ///< The initial marking std::vector tokens_n; ///< The current marking std::vector active_transitions_n; ///< List of active transitions Eventlog event_log; ///< The most actual event_log diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index de411003..dfeeaad5 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -53,12 +53,14 @@ bool areAllTransitionsInStore(const Store &store, const Net &net) noexcept { * */ struct Petri { + Petri(Petri const &) = delete; + Petri(Petri &&) noexcept = delete; + Petri &operator=(Petri const &) = delete; + Petri &operator=(Petri &&) noexcept = delete; + Model m; ///< The Petri net model std::atomic> - thread_id_; ///< The id of the thread from which run is called. - const Marking m0_; ///< The initial marking for this instance - const Net net_; ///< The net - const PriorityTable priorities_; ///< The priority table for this instance + thread_id_; ///< The id of the thread from which run is called. const std::vector final_marking; ///< The net will stop queueing reducers ///< once the marking has been reached @@ -87,9 +89,6 @@ struct Petri { const PriorityTable &priorities, const std::string &case_id) : m(net, store, priorities, m0), thread_id_(std::nullopt), - m0_(m0), - net_(net), - priorities_(priorities), final_marking(m.toTokens(final_marking)), stp(stp), reducers({std::make_shared>(32), @@ -146,17 +145,15 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, auto impl = std::make_shared(net, m0, stp, final_marking, store, priority, case_id); return {impl, [=](const Transition &t) { - if (impl->thread_id_.load()) { - const auto &reducer = impl->reducers[impl->reducer_selector]; - reducer->enqueue([=](Model &&m) { - const auto t_index = toIndex(m.net.transition, t); - m.active_transitions_n.push_back(t_index); - reducer->enqueue(fireTransition( - t_index, m.net.transition[t_index], m.net.store[t_index], - impl->case_id, reducer)); - return std::ref(m); - }); - } + const auto &reducer = impl->reducers[impl->reducer_selector]; + reducer->enqueue([=](Model &&m) { + const auto t_index = toIndex(m.net.transition, t); + m.active_transitions_n.push_back(t_index); + reducer->enqueue( + fireTransition(t_index, m.net.transition[t_index], + m.net.store[t_index], impl->case_id, reducer)); + return std::ref(m); + }); }}; } From 7a74ba53faac66a82b990e72a579e13ece8b0e27 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Mon, 31 Jul 2023 20:44:00 +0200 Subject: [PATCH 11/27] refactor with state --- symmetri/include/symmetri/types.h | 1 + symmetri/model.cc | 2 +- symmetri/model.h | 2 +- symmetri/symmetri.cc | 36 +++++++++++++++---------------- symmetri/types.cpp | 3 +++ 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/symmetri/include/symmetri/types.h b/symmetri/include/symmetri/types.h index 228864ff..644c1c43 100644 --- a/symmetri/include/symmetri/types.h +++ b/symmetri/include/symmetri/types.h @@ -24,6 +24,7 @@ enum class State { Completed, ///< The transition completed as expected Deadlock, ///< The transition deadlocked UserExit, ///< The transition or interrupted and possibly stopped + Paused, ///< The transition is paused Error ///< None of the above }; diff --git a/symmetri/model.cc b/symmetri/model.cc index 1f29d4cb..239f42c4 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -83,7 +83,7 @@ std::vector createPriorityLookup( Model::Model(const Net &_net, const Store &store, const PriorityTable &_priority, const Marking &M0) - : event_log({}), is_paused(false) { + : event_log({}), state(State::Scheduled) { event_log.reserve(1000); std::tie(net.transition, net.place, net.store) = convert(_net, store); std::tie(net.input_n, net.output_n) = populateIoLookups(_net, net.place); diff --git a/symmetri/model.h b/symmetri/model.h index 689137df..b4c2d466 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -195,7 +195,7 @@ struct Model { std::vector tokens_n; ///< The current marking std::vector active_transitions_n; ///< List of active transitions Eventlog event_log; ///< The most actual event_log - bool is_paused; + State state; private: /** diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index dfeeaad5..3c9cadb5 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -68,8 +68,6 @@ struct Petri { std::array>, 2> reducers; unsigned int reducer_selector; std::string case_id; ///< The case id of this particular Petri instance - std::atomic early_exit; ///< once it is true, no more new transitions - ///< will be queued and the run will exit. /** * @brief Construct a new Petri object. Most importantly, it also creates the @@ -94,8 +92,7 @@ struct Petri { reducers({std::make_shared>(32), std::make_shared>(32)}), reducer_selector(0), - case_id(case_id), - early_exit(false) {} + case_id(case_id) {} const std::shared_ptr> &setFreshQueue() { // increment index to get the already prepared queue. @@ -230,7 +227,6 @@ symmetri::Result fire(const PetriNet &app) { // we are running! auto &active_reducers = petri.reducers[petri.reducer_selector]; petri.thread_id_.store(symmetri::getThreadId()); - petri.early_exit.store(false); m.event_log.clear(); m.tokens_n = m.initial_tokens; @@ -239,26 +235,29 @@ symmetri::Result fire(const PetriNet &app) { // start! m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); // get a reducer. Immediately, or wait a bit - while ((m.active_transitions_n.size() > 0 || m.is_paused) && + while ((m.active_transitions_n.size() > 0 || m.state == State::Paused) && active_reducers->wait_dequeue_timed(f, -1)) { do { m = f(std::move(m)); - } while (!petri.early_exit.load() && active_reducers->try_dequeue(f)); + } while (m.state != State::UserExit && active_reducers->try_dequeue(f)); - if (symmetri::MarkingReached(m.tokens_n, petri.final_marking) || - petri.early_exit.load()) { + m.state = symmetri::MarkingReached(m.tokens_n, petri.final_marking) + ? State::Completed + : m.state; + + if (m.state == State::Completed || m.state == State::UserExit) { // we're done break; - } else if (!m.is_paused && !waiting_for_resume) { + } else if (m.state != State::Paused && !waiting_for_resume) { // we're firing m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); - } else if (m.is_paused && !waiting_for_resume) { + } else if (m.state == State::Paused && !waiting_for_resume) { // we've been asked to pause waiting_for_resume = true; for (const auto transition_index : m.active_transitions_n) { pause(m.net.store.at(transition_index)); } - } else if (!m.is_paused && waiting_for_resume) { + } else if (m.state != State::Paused && waiting_for_resume) { // we've been asked to resume waiting_for_resume = false; m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); @@ -270,7 +269,7 @@ symmetri::Result fire(const PetriNet &app) { // determine what was the reason we terminated. symmetri::State result; - if (petri.early_exit.load()) { + if (m.state == State::UserExit) { { // populate that eventlog with child eventlog and possible cancelations. for (const auto transition_index : m.active_transitions_n) { @@ -307,14 +306,13 @@ symmetri::Result fire(const PetriNet &app) { symmetri::Result cancel(const PetriNet &app) { const auto &impl = *app.impl; const auto maybe_thread_id = impl.thread_id_.load(); - if (maybe_thread_id && maybe_thread_id.value() != symmetri::getThreadId() && - !impl.early_exit.load()) { + if (maybe_thread_id && maybe_thread_id.value() != symmetri::getThreadId()) { std::mutex m; std::condition_variable cv; bool ready = false; impl.reducers[impl.reducer_selector]->enqueue( [=, &m, &cv, &ready](symmetri::Model &&model) { - app.impl->early_exit.store(true); + app.impl->m.state = symmetri::State::UserExit; { std::lock_guard lk(m); ready = true; @@ -326,7 +324,7 @@ symmetri::Result cancel(const PetriNet &app) { cv.wait(lk, [&] { return ready; }); } else { impl.reducers[impl.reducer_selector]->enqueue([=](symmetri::Model &&model) { - app.impl->early_exit.store(true); + app.impl->m.state = symmetri::State::UserExit; return std::ref(model); }); } @@ -337,7 +335,7 @@ symmetri::Result cancel(const PetriNet &app) { void pause(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( [&](symmetri::Model &&model) { - model.is_paused = true; + model.state = State::Paused; return std::ref(model); }); }; @@ -345,7 +343,7 @@ void pause(const PetriNet &app) { void resume(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( [&](symmetri::Model &&model) { - model.is_paused = false; + model.state = State::Started; return std::ref(model); }); }; diff --git a/symmetri/types.cpp b/symmetri/types.cpp index 9a411fff..01cc1f72 100644 --- a/symmetri/types.cpp +++ b/symmetri/types.cpp @@ -121,6 +121,9 @@ std::string printState(symmetri::State s) noexcept { case State::UserExit: ret = "UserExit"; break; + case State::Paused: + ret = "Paused"; + break; case State::Error: ret = "Error"; break; From c11d0396abea3f6f5f92358e6e0611cecb915543 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Tue, 1 Aug 2023 17:24:43 +0200 Subject: [PATCH 12/27] nice refactor of fire loop --- examples/flight/flight.cc | 4 +- symmetri/CMakeLists.txt | 4 +- symmetri/symmetri.cc | 124 +++++++++++--------------------------- 3 files changed, 40 insertions(+), 92 deletions(-) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index df7bd7b6..8b9b52d3 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -85,10 +85,11 @@ int main(int, char *argv[]) { // a thread that polls the eventlog and writes it to a file auto gantt = std::thread([&] { while (running) { - writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(getLog(bignet))); std::this_thread::sleep_for(std::chrono::seconds(3)); + writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(getLog(bignet))); } writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(getLog(bignet))); + std::cout << "gannt blocked" << std::endl; }); // Parallel to the PetriNet execution, we run a thread through which we @@ -116,6 +117,7 @@ int main(int, char *argv[]) { } std::cin.get(); }; + std::cout << "cin blocked" << std::endl; }); // this is where we call the blocking fire-function that executes the petri diff --git a/symmetri/CMakeLists.txt b/symmetri/CMakeLists.txt index 1dd0df0f..3c2bb4bb 100644 --- a/symmetri/CMakeLists.txt +++ b/symmetri/CMakeLists.txt @@ -1,5 +1,3 @@ -find_package(spdlog REQUIRED) - include(FetchContent) FetchContent_Declare( @@ -31,7 +29,7 @@ add_library(${PROJECT_NAME} SHARED submodules/tinyxml2/tinyxml2.cpp ) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -target_link_libraries(${PROJECT_NAME} PUBLIC pthread PRIVATE concurrentqueue spdlog::spdlog) +target_link_libraries(${PROJECT_NAME} PUBLIC pthread PRIVATE concurrentqueue) include(GenerateExportHeader) generate_export_header(${PROJECT_NAME}) diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 3c9cadb5..e18b9dd5 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -1,7 +1,6 @@ #include "symmetri/symmetri.h" #include -#include #include #include @@ -40,7 +39,7 @@ bool areAllTransitionsInStore(const Store &store, const Net &net) noexcept { return e.first == t; }) != store.end(); if (!store_has_transition) { - spdlog::error("Transition {0} is not in store", t); + // error seriously here or default? } return store_has_transition; }); @@ -155,6 +154,7 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, } } // namespace symmetri + using namespace symmetri; PetriNet::PetriNet(const std::set &files, @@ -209,88 +209,40 @@ bool PetriNet::reuseApplication(const std::string &new_case_id) { } symmetri::Result fire(const PetriNet &app) { - if (app.impl == nullptr) { - spdlog::error("Something went seriously wrong. Please send a bug report."); + if (app.impl == nullptr || app.impl->thread_id_.load()) { return {{}, symmetri::State::Error}; } auto &petri = *app.impl; auto &m = petri.m; - if (app.impl->thread_id_.load()) { - spdlog::warn( - "[{0}] is already active, it can not run it again before it is " - "finished.", - app.impl->case_id); - return {{}, symmetri::State::Error}; - } // we are running! auto &active_reducers = petri.reducers[petri.reducer_selector]; petri.thread_id_.store(symmetri::getThreadId()); m.event_log.clear(); m.tokens_n = m.initial_tokens; - - bool waiting_for_resume = false; + m.state = State::Started; symmetri::Reducer f; // start! m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); - // get a reducer. Immediately, or wait a bit - while ((m.active_transitions_n.size() > 0 || m.state == State::Paused) && + while ((m.state == State::Started || m.state == State::Paused) && active_reducers->wait_dequeue_timed(f, -1)) { do { m = f(std::move(m)); - } while (m.state != State::UserExit && active_reducers->try_dequeue(f)); + } while (active_reducers->try_dequeue(f)); m.state = symmetri::MarkingReached(m.tokens_n, petri.final_marking) ? State::Completed : m.state; - if (m.state == State::Completed || m.state == State::UserExit) { - // we're done - break; - } else if (m.state != State::Paused && !waiting_for_resume) { + if (m.state == State::Started) { // we're firing m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); - } else if (m.state == State::Paused && !waiting_for_resume) { - // we've been asked to pause - waiting_for_resume = true; - for (const auto transition_index : m.active_transitions_n) { - pause(m.net.store.at(transition_index)); - } - } else if (m.state != State::Paused && waiting_for_resume) { - // we've been asked to resume - waiting_for_resume = false; - m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); - for (const auto transition_index : m.active_transitions_n) { - resume(m.net.store.at(transition_index)); - } + // if there's nothing to fire; we deadlocked + m.state = m.active_transitions_n.size() == 0 ? State::Deadlock : m.state; } } - // determine what was the reason we terminated. - symmetri::State result; - if (m.state == State::UserExit) { - { - // populate that eventlog with child eventlog and possible cancelations. - for (const auto transition_index : m.active_transitions_n) { - auto [el, state] = cancel(m.net.store.at(transition_index)); - if (!el.empty()) { - m.event_log.insert(m.event_log.end(), el.begin(), el.end()); - } - m.event_log.push_back({petri.case_id, - m.net.transition[transition_index], state, - symmetri::Clock::now()}); - } - result = symmetri::State::UserExit; - } - } else if (symmetri::MarkingReached(m.tokens_n, petri.final_marking)) { - result = symmetri::State::Completed; - } else if (m.active_transitions_n.empty()) { - result = symmetri::State::Deadlock; - } else { - result = symmetri::State::Error; - } - // empty reducers m.active_transitions_n.clear(); while (active_reducers->try_dequeue(f)) { @@ -300,58 +252,55 @@ symmetri::Result fire(const PetriNet &app) { petri.thread_id_.store(std::nullopt); petri.setFreshQueue(); - return {m.event_log, result}; + return {m.event_log, m.state}; }; symmetri::Result cancel(const PetriNet &app) { const auto &impl = *app.impl; - const auto maybe_thread_id = impl.thread_id_.load(); - if (maybe_thread_id && maybe_thread_id.value() != symmetri::getThreadId()) { - std::mutex m; - std::condition_variable cv; - bool ready = false; - impl.reducers[impl.reducer_selector]->enqueue( - [=, &m, &cv, &ready](symmetri::Model &&model) { - app.impl->m.state = symmetri::State::UserExit; - { - std::lock_guard lk(m); - ready = true; - } - cv.notify_one(); - return std::ref(model); - }); - std::unique_lock lk(m); - cv.wait(lk, [&] { return ready; }); - } else { - impl.reducers[impl.reducer_selector]->enqueue([=](symmetri::Model &&model) { - app.impl->m.state = symmetri::State::UserExit; - return std::ref(model); - }); - } + impl.reducers[impl.reducer_selector]->enqueue([=](symmetri::Model &&model) { + model.state = symmetri::State::UserExit; + // populate that eventlog with child eventlog and possible cancelations. + for (const auto transition_index : model.active_transitions_n) { + auto [el, state] = cancel(model.net.store.at(transition_index)); + if (!el.empty()) { + model.event_log.insert(model.event_log.end(), el.begin(), el.end()); + } + model.event_log.push_back({app.impl->case_id, + model.net.transition[transition_index], state, + symmetri::Clock::now()}); + } + return std::ref(model); + }); return {getLog(app), symmetri::State::UserExit}; } void pause(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( - [&](symmetri::Model &&model) { + [](symmetri::Model &&model) { model.state = State::Paused; + for (const auto transition_index : model.active_transitions_n) { + pause(model.net.store.at(transition_index)); + } return std::ref(model); }); }; void resume(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( - [&](symmetri::Model &&model) { + [](symmetri::Model &&model) { model.state = State::Started; + for (const auto transition_index : model.active_transitions_n) { + resume(model.net.store.at(transition_index)); + } return std::ref(model); }); }; symmetri::Eventlog getLog(const PetriNet &app) { - const auto &impl = *app.impl; - const auto maybe_thread_id = impl.thread_id_.load(); - if (maybe_thread_id && maybe_thread_id.value() != getThreadId()) { + const auto maybe_thread_id = app.impl->thread_id_.load(); + if (maybe_thread_id && app.impl->thread_id_.load().value() != getThreadId()) { + const auto &impl = *app.impl; std::promise el; std::future el_getter = el.get_future(); impl.reducers[impl.reducer_selector]->enqueue([&](Model &&model) { @@ -368,7 +317,6 @@ symmetri::Eventlog getLog(const PetriNet &app) { }); return el_getter.get(); } else { - return impl.m.event_log; + return app.impl->m.event_log; } - getLog(app); } From e55a7b11ebfdb3f1db03931251343b2bb744ff23 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Wed, 2 Aug 2023 08:14:34 +0200 Subject: [PATCH 13/27] remove html from ignore --- .gitignore | 1 - examples/flight/mermaid.html | 0 2 files changed, 1 deletion(-) delete mode 100644 examples/flight/mermaid.html diff --git a/.gitignore b/.gitignore index 94526814..432de573 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ build install docs/html .vscode -examples/flight/mermaid.html # Prerequisites *.d diff --git a/examples/flight/mermaid.html b/examples/flight/mermaid.html deleted file mode 100644 index e69de29b..00000000 From ebac56cf96b08f238a6cecbbfab4481e862f3e47 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 08:01:19 +0200 Subject: [PATCH 14/27] move to model --- symmetri/model.cc | 6 ++++-- symmetri/model.h | 5 ++++- symmetri/symmetri.cc | 21 +++++++++------------ symmetri/tests/test_bugs.cc | 2 +- symmetri/tests/test_model.cc | 10 +++++----- symmetri/tests/test_priorities.cc | 4 ++-- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/symmetri/model.cc b/symmetri/model.cc index 239f42c4..f27916f8 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -82,8 +82,9 @@ std::vector createPriorityLookup( } Model::Model(const Net &_net, const Store &store, - const PriorityTable &_priority, const Marking &M0) - : event_log({}), state(State::Scheduled) { + const PriorityTable &_priority, const Marking &M0, + const Marking &final_marking, const std::string &case_id) + : event_log({}), state(State::Scheduled), case_id(case_id) { event_log.reserve(1000); std::tie(net.transition, net.place, net.store) = convert(_net, store); std::tie(net.input_n, net.output_n) = populateIoLookups(_net, net.place); @@ -92,6 +93,7 @@ Model::Model(const Net &_net, const Store &store, net.priority = createPriorityLookup(net.transition, _priority); initial_tokens = toTokens(M0); tokens_n = initial_tokens; + final_marking_n = toTokens(final_marking); } std::vector Model::toTokens(const Marking &marking) const noexcept { diff --git a/symmetri/model.h b/symmetri/model.h index b4c2d466..d0152f17 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -91,7 +91,8 @@ struct Model { * @param M0 */ explicit Model(const Net &net, const Store &store, - const PriorityTable &priority, const Marking &M0); + const PriorityTable &priority, const Marking &M0, + const Marking &final_marking, const std::string &case_id); ~Model() noexcept = default; Model(Model const &) = delete; Model(Model &&) noexcept = delete; @@ -193,9 +194,11 @@ struct Model { std::vector initial_tokens; ///< The initial marking std::vector tokens_n; ///< The current marking + std::vector final_marking_n; ///< The final marking std::vector active_transitions_n; ///< List of active transitions Eventlog event_log; ///< The most actual event_log State state; + std::string case_id; private: /** diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index e18b9dd5..0dbcaf7e 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -66,7 +66,6 @@ struct Petri { const std::shared_ptr stp; std::array>, 2> reducers; unsigned int reducer_selector; - std::string case_id; ///< The case id of this particular Petri instance /** * @brief Construct a new Petri object. Most importantly, it also creates the @@ -84,14 +83,12 @@ struct Petri { Petri(const Net &net, const Marking &m0, std::shared_ptr stp, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id) - : m(net, store, priorities, m0), + : m(net, store, priorities, m0, final_marking, case_id), thread_id_(std::nullopt), - final_marking(m.toTokens(final_marking)), stp(stp), reducers({std::make_shared>(32), std::make_shared>(32)}), - reducer_selector(0), - case_id(case_id) {} + reducer_selector(0) {} const std::shared_ptr> &setFreshQueue() { // increment index to get the already prepared queue. @@ -147,7 +144,7 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, m.active_transitions_n.push_back(t_index); reducer->enqueue( fireTransition(t_index, m.net.transition[t_index], - m.net.store[t_index], impl->case_id, reducer)); + m.net.store[t_index], m.case_id, reducer)); return std::ref(m); }); }}; @@ -197,10 +194,10 @@ std::function PetriNet::registerTransitionCallback( bool PetriNet::reuseApplication(const std::string &new_case_id) { if (impl) { auto &petri = *impl; - if (petri.thread_id_.load().has_value() || new_case_id == petri.case_id) { + if (petri.thread_id_.load().has_value() || new_case_id == petri.m.case_id) { return false; } - petri.case_id = new_case_id; + petri.m.case_id = new_case_id; petri.thread_id_.store(std::nullopt); return true; } else { @@ -224,20 +221,20 @@ symmetri::Result fire(const PetriNet &app) { m.state = State::Started; symmetri::Reducer f; // start! - m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); + m.fireTransitions(active_reducers, petri.stp, true, petri.m.case_id); while ((m.state == State::Started || m.state == State::Paused) && active_reducers->wait_dequeue_timed(f, -1)) { do { m = f(std::move(m)); } while (active_reducers->try_dequeue(f)); - m.state = symmetri::MarkingReached(m.tokens_n, petri.final_marking) + m.state = symmetri::MarkingReached(m.tokens_n, m.final_marking_n) ? State::Completed : m.state; if (m.state == State::Started) { // we're firing - m.fireTransitions(active_reducers, petri.stp, true, petri.case_id); + m.fireTransitions(active_reducers, petri.stp, true, petri.m.case_id); // if there's nothing to fire; we deadlocked m.state = m.active_transitions_n.size() == 0 ? State::Deadlock : m.state; } @@ -265,7 +262,7 @@ symmetri::Result cancel(const PetriNet &app) { if (!el.empty()) { model.event_log.insert(model.event_log.end(), el.begin(), el.end()); } - model.event_log.push_back({app.impl->case_id, + model.event_log.push_back({model.case_id, model.net.transition[transition_index], state, symmetri::Clock::now()}); } diff --git a/symmetri/tests/test_bugs.cc b/symmetri/tests/test_bugs.cc index a4f1379c..91fe9a7a 100644 --- a/symmetri/tests/test_bugs.cc +++ b/symmetri/tests/test_bugs.cc @@ -33,7 +33,7 @@ std::tuple testNet() { TEST_CASE("Firing the same transition before it can complete should work") { auto [net, store, priority, m0] = testNet(); - Model m(net, store, priority, m0); + Model m(net, store, priority, m0, {}, "s"); auto reducers = std::make_shared>(4); diff --git a/symmetri/tests/test_model.cc b/symmetri/tests/test_model.cc index ff6e7e78..e08c150f 100644 --- a/symmetri/tests/test_model.cc +++ b/symmetri/tests/test_model.cc @@ -49,7 +49,7 @@ TEST_CASE("Test equaliy of nets") { TEST_CASE("Run one transition iteration in a petri net") { auto [net, store, priority, m0] = testNet(); - Model m(net, store, priority, m0); + Model m(net, store, priority, m0, {}, "s"); auto stp = std::make_shared(1); auto reducers = std::make_shared>(4); @@ -88,7 +88,7 @@ TEST_CASE("Run until net dies") { using namespace moodycamel; auto [net, store, priority, m0] = testNet(); - Model m(net, store, priority, m0); + Model m(net, store, priority, m0, {}, "s"); auto reducers = std::make_shared>(4); @@ -118,7 +118,7 @@ TEST_CASE("Run until net dies with nullptr") { auto [net, store, priority, m0] = testNet(); store = {{"t0", DirectMutation{}}, {"t1", DirectMutation{}}}; - Model m(net, store, priority, m0); + Model m(net, store, priority, m0, {}, "s"); auto reducers = std::make_shared>(4); auto stp = std::make_shared(1); @@ -154,7 +154,7 @@ TEST_CASE( } // with this initial marking, all but transition e are possible. Marking m0 = {{"Pa", 1}}; - Model m(net, store, {}, m0); + Model m(net, store, {}, m0, {}, "s"); auto fireable_transitions = m.getFireableTransitions(); auto find = [&](auto a) { return std::find(fireable_transitions.begin(), fireable_transitions.end(), @@ -184,7 +184,7 @@ TEST_CASE("Step through transitions") { auto stp = std::make_shared(1); // with this initial marking, all but transition e are possible. Marking m0 = {{"Pa", 4}}; - Model m(net, store, {}, m0); + Model m(net, store, {}, m0, {}, "s"); REQUIRE(m.getFireableTransitions().size() == 4); // abcd m.fire("e", reducers, stp); m.fire("b", reducers, stp); diff --git a/symmetri/tests/test_priorities.cc b/symmetri/tests/test_priorities.cc index c5e1ca48..abe5624b 100644 --- a/symmetri/tests/test_priorities.cc +++ b/symmetri/tests/test_priorities.cc @@ -21,7 +21,7 @@ TEST_CASE( Marking m0 = {{"Pa", 1}, {"Pb", 0}, {"Pc", 0}}; auto stp = std::make_shared(1); - auto m = Model(net, store, priority, m0); + auto m = Model(net, store, priority, m0, {}, "s"); m.fireTransitions(reducers, stp, true); Reducer r; @@ -54,7 +54,7 @@ TEST_CASE("Using nullptr does not queue reducers.") { Marking m0 = {{"Pa", 1}, {"Pb", 0}, {"Pc", 0}}; auto stp = std::make_shared(1); - auto m = Model(net, store, priority, m0); + auto m = Model(net, store, priority, m0, {}, "s"); m.fireTransitions(reducers, stp, true); // no reducers needed, as simple transitions are handled within run all. auto prio_t0 = std::find_if(priority.begin(), priority.end(), [](auto e) { From 5381343ca66ab7a5ebe92d580d3ae3baef873ec4 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 08:42:53 +0200 Subject: [PATCH 15/27] remove remaining members --- symmetri/model.cc | 35 +++++++++--- symmetri/model.h | 14 +++-- symmetri/symmetri.cc | 91 +++++++++---------------------- symmetri/tests/test_bugs.cc | 7 +-- symmetri/tests/test_model.cc | 33 +++++------ symmetri/tests/test_priorities.cc | 8 +-- 6 files changed, 85 insertions(+), 103 deletions(-) diff --git a/symmetri/model.cc b/symmetri/model.cc index f27916f8..8322f6bd 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -83,8 +83,16 @@ std::vector createPriorityLookup( Model::Model(const Net &_net, const Store &store, const PriorityTable &_priority, const Marking &M0, - const Marking &final_marking, const std::string &case_id) - : event_log({}), state(State::Scheduled), case_id(case_id) { + const Marking &final_marking, const std::string &case_id, + std::shared_ptr stp) + : event_log({}), + state(State::Scheduled), + case_id(case_id), + reducers( + {std::make_shared>(32), + std::make_shared>(32)}), + reducer_selector(0), + pool(stp) { event_log.reserve(1000); std::tie(net.transition, net.place, net.store) = convert(_net, store); std::tie(net.input_n, net.output_n) = populateIoLookups(_net, net.place); @@ -124,7 +132,7 @@ bool Model::Fire( const size_t t, const std::shared_ptr> &reducers, - const std::shared_ptr &pool, const std::string &case_id) { + const std::string &case_id) { auto timestamp = Clock::now(); // deduct the marking for (const size_t place : net.input_n[t]) { @@ -158,20 +166,18 @@ bool Model::fire( const Transition &t, const std::shared_ptr> &reducers, - const std::shared_ptr &pool, const std::string &case_id) { + const std::string &case_id) { auto it = std::find(net.transition.begin(), net.transition.end(), t); return it != net.transition.end() && canFire(net.input_n[std::distance(net.transition.begin(), it)], tokens_n) && - !Fire(std::distance(net.transition.begin(), it), reducers, pool, - case_id); + !Fire(std::distance(net.transition.begin(), it), reducers, case_id); } void Model::fireTransitions( const std::shared_ptr> &reducers, - const std::shared_ptr &pool, bool run_all, - const std::string &case_id) { + bool run_all, const std::string &case_id) { // find possible transitions auto possible_transition_list_n = possibleTransitions(tokens_n, net.p_to_ts_n, net.priority); @@ -193,7 +199,7 @@ void Model::fireTransitions( do { // if Fire returns true, update the possible transition list - if (Fire(possible_transition_list_n.front(), reducers, pool, case_id)) { + if (Fire(possible_transition_list_n.front(), reducers, case_id)) { possible_transition_list_n = possibleTransitions(tokens_n, net.p_to_ts_n, net.priority); } @@ -225,4 +231,15 @@ std::vector Model::getActiveTransitions() const { return transition_list; } +const std::shared_ptr> + &Model::setFreshQueue() { + // increment index to get the already prepared queue. + reducer_selector = reducer_selector > 0 ? 0 : 1; + // create a new queue for later use at the next index. + pool->push([reducer = reducers[reducer_selector > 0 ? 0 : 1]]() mutable { + reducer.reset(new moodycamel::BlockingConcurrentQueue{32}); + }); + return reducers[reducer_selector]; +} + } // namespace symmetri diff --git a/symmetri/model.h b/symmetri/model.h index d0152f17..06f2786c 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -92,7 +92,8 @@ struct Model { */ explicit Model(const Net &net, const Store &store, const PriorityTable &priority, const Marking &M0, - const Marking &final_marking, const std::string &case_id); + const Marking &final_marking, const std::string &case_id, + std::shared_ptr stp); ~Model() noexcept = default; Model(Model const &) = delete; Model(Model &&) noexcept = delete; @@ -151,7 +152,6 @@ struct Model { bool fire(const Transition &t, const std::shared_ptr> &reducers, - const std::shared_ptr &polymorphic_actions, const std::string &case_id = "undefined_case_id"); /** @@ -167,7 +167,6 @@ struct Model { void fireTransitions( const std::shared_ptr> &reducers, - const std::shared_ptr &polymorphic_actions, bool run_all = true, const std::string &case_id = "undefined_case_id"); struct { @@ -200,6 +199,14 @@ struct Model { State state; std::string case_id; + std::array>, 2> + reducers; + unsigned int reducer_selector; + std::shared_ptr pool; + + const std::shared_ptr> + &setFreshQueue(); + private: /** * @brief fires a transition. @@ -214,7 +221,6 @@ struct Model { bool Fire(const size_t t, const std::shared_ptr> &reducers, - const std::shared_ptr &polymorphic_actions, const std::string &case_id); }; diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 0dbcaf7e..35fe66ef 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -1,16 +1,10 @@ #include "symmetri/symmetri.h" -#include - -#include #include #include #include -#include #include -#include #include -#include #include "model.h" #include "symmetri/parsers.h" @@ -60,12 +54,6 @@ struct Petri { Model m; ///< The Petri net model std::atomic> thread_id_; ///< The id of the thread from which run is called. - const std::vector - final_marking; ///< The net will stop queueing reducers - ///< once the marking has been reached - const std::shared_ptr stp; - std::array>, 2> reducers; - unsigned int reducer_selector; /** * @brief Construct a new Petri object. Most importantly, it also creates the @@ -83,38 +71,8 @@ struct Petri { Petri(const Net &net, const Marking &m0, std::shared_ptr stp, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id) - : m(net, store, priorities, m0, final_marking, case_id), - thread_id_(std::nullopt), - stp(stp), - reducers({std::make_shared>(32), - std::make_shared>(32)}), - reducer_selector(0) {} - - const std::shared_ptr> &setFreshQueue() { - // increment index to get the already prepared queue. - reducer_selector = reducer_selector > 0 ? 0 : 1; - // create a new queue for later use at the next index. - stp->push([reducer = reducers[reducer_selector > 0 ? 0 : 1]]() mutable { - reducer.reset(new BlockingConcurrentQueue{32}); - }); - return reducers[reducer_selector]; - } - - std::vector getFireableTransitions() const noexcept { - const auto maybe_thread_id = thread_id_.load(); - if (maybe_thread_id && maybe_thread_id.value() != getThreadId()) { - std::promise> transitions; - std::future> transitions_getter = - transitions.get_future(); - reducers[reducer_selector]->enqueue([&](Model &&model) { - transitions.set_value(model.getFireableTransitions()); - return std::ref(model); - }); - return transitions_getter.get(); - } else { - return m.getFireableTransitions(); - } - }; + : m(net, store, priorities, m0, final_marking, case_id, stp), + thread_id_(std::nullopt) {} }; /** @@ -138,7 +96,7 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, auto impl = std::make_shared(net, m0, stp, final_marking, store, priority, case_id); return {impl, [=](const Transition &t) { - const auto &reducer = impl->reducers[impl->reducer_selector]; + const auto &reducer = impl->m.reducers[impl->m.reducer_selector]; reducer->enqueue([=](Model &&m) { const auto t_index = toIndex(m.net.transition, t); m.active_transitions_n.push_back(t_index); @@ -214,14 +172,14 @@ symmetri::Result fire(const PetriNet &app) { auto &m = petri.m; // we are running! - auto &active_reducers = petri.reducers[petri.reducer_selector]; + auto &active_reducers = petri.m.reducers[petri.m.reducer_selector]; petri.thread_id_.store(symmetri::getThreadId()); m.event_log.clear(); m.tokens_n = m.initial_tokens; m.state = State::Started; symmetri::Reducer f; // start! - m.fireTransitions(active_reducers, petri.stp, true, petri.m.case_id); + m.fireTransitions(active_reducers, true, petri.m.case_id); while ((m.state == State::Started || m.state == State::Paused) && active_reducers->wait_dequeue_timed(f, -1)) { do { @@ -234,7 +192,7 @@ symmetri::Result fire(const PetriNet &app) { if (m.state == State::Started) { // we're firing - m.fireTransitions(active_reducers, petri.stp, true, petri.m.case_id); + m.fireTransitions(active_reducers, true, petri.m.case_id); // if there's nothing to fire; we deadlocked m.state = m.active_transitions_n.size() == 0 ? State::Deadlock : m.state; } @@ -247,33 +205,34 @@ symmetri::Result fire(const PetriNet &app) { } petri.thread_id_.store(std::nullopt); - petri.setFreshQueue(); + petri.m.setFreshQueue(); return {m.event_log, m.state}; }; symmetri::Result cancel(const PetriNet &app) { const auto &impl = *app.impl; - impl.reducers[impl.reducer_selector]->enqueue([=](symmetri::Model &&model) { - model.state = symmetri::State::UserExit; - // populate that eventlog with child eventlog and possible cancelations. - for (const auto transition_index : model.active_transitions_n) { - auto [el, state] = cancel(model.net.store.at(transition_index)); - if (!el.empty()) { - model.event_log.insert(model.event_log.end(), el.begin(), el.end()); - } - model.event_log.push_back({model.case_id, - model.net.transition[transition_index], state, - symmetri::Clock::now()}); - } - return std::ref(model); - }); + impl.m.reducers[impl.m.reducer_selector]->enqueue( + [=](symmetri::Model &&model) { + model.state = symmetri::State::UserExit; + // populate that eventlog with child eventlog and possible cancelations. + for (const auto transition_index : model.active_transitions_n) { + auto [el, state] = cancel(model.net.store.at(transition_index)); + if (!el.empty()) { + model.event_log.insert(model.event_log.end(), el.begin(), el.end()); + } + model.event_log.push_back({model.case_id, + model.net.transition[transition_index], + state, symmetri::Clock::now()}); + } + return std::ref(model); + }); return {getLog(app), symmetri::State::UserExit}; } void pause(const PetriNet &app) { - app.impl->reducers[app.impl->reducer_selector]->enqueue( + app.impl->m.reducers[app.impl->m.reducer_selector]->enqueue( [](symmetri::Model &&model) { model.state = State::Paused; for (const auto transition_index : model.active_transitions_n) { @@ -284,7 +243,7 @@ void pause(const PetriNet &app) { }; void resume(const PetriNet &app) { - app.impl->reducers[app.impl->reducer_selector]->enqueue( + app.impl->m.reducers[app.impl->m.reducer_selector]->enqueue( [](symmetri::Model &&model) { model.state = State::Started; for (const auto transition_index : model.active_transitions_n) { @@ -300,7 +259,7 @@ symmetri::Eventlog getLog(const PetriNet &app) { const auto &impl = *app.impl; std::promise el; std::future el_getter = el.get_future(); - impl.reducers[impl.reducer_selector]->enqueue([&](Model &&model) { + impl.m.reducers[impl.m.reducer_selector]->enqueue([&](Model &&model) { auto eventlog = model.event_log; // get event log from parent nets: for (size_t pt_idx : model.active_transitions_n) { diff --git a/symmetri/tests/test_bugs.cc b/symmetri/tests/test_bugs.cc index 91fe9a7a..57b0ba60 100644 --- a/symmetri/tests/test_bugs.cc +++ b/symmetri/tests/test_bugs.cc @@ -33,13 +33,12 @@ std::tuple testNet() { TEST_CASE("Firing the same transition before it can complete should work") { auto [net, store, priority, m0] = testNet(); - Model m(net, store, priority, m0, {}, "s"); - + auto stp = std::make_shared(2); + Model m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); - auto stp = std::make_shared(2); REQUIRE(m.active_transitions_n.empty()); - m.fireTransitions(reducers, stp, true); + m.fireTransitions(reducers, true); REQUIRE(m.getMarking().empty()); CHECK(m.getActiveTransitions() == std::vector{"t", "t"}); diff --git a/symmetri/tests/test_model.cc b/symmetri/tests/test_model.cc index e08c150f..7c87541d 100644 --- a/symmetri/tests/test_model.cc +++ b/symmetri/tests/test_model.cc @@ -49,12 +49,12 @@ TEST_CASE("Test equaliy of nets") { TEST_CASE("Run one transition iteration in a petri net") { auto [net, store, priority, m0] = testNet(); - Model m(net, store, priority, m0, {}, "s"); auto stp = std::make_shared(1); + Model m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); // t0 is enabled. - m.fireTransitions(reducers, stp, true, ""); + m.fireTransitions(reducers, true, ""); // t0 is dispatched but it's reducer has not yet run, so pre-conditions are // processed but post are not: REQUIRE(m.getActiveTransitions() == @@ -88,12 +88,11 @@ TEST_CASE("Run until net dies") { using namespace moodycamel; auto [net, store, priority, m0] = testNet(); - Model m(net, store, priority, m0, {}, "s"); + auto stp = std::make_shared(1); + Model m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); - auto stp = std::make_shared(1); - Reducer r; PolyTransition a([] {}); // we need to enqueue one 'no-operation' to start the live net. @@ -101,7 +100,7 @@ TEST_CASE("Run until net dies") { do { if (reducers->try_dequeue(r)) { m = r(std::move(m)); - m.fireTransitions(reducers, stp, true); + m.fireTransitions(reducers, true); } } while (m.active_transitions_n.size() > 0); @@ -118,10 +117,10 @@ TEST_CASE("Run until net dies with nullptr") { auto [net, store, priority, m0] = testNet(); store = {{"t0", DirectMutation{}}, {"t1", DirectMutation{}}}; - Model m(net, store, priority, m0, {}, "s"); + auto stp = std::make_shared(1); + Model m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); - auto stp = std::make_shared(1); Reducer r; PolyTransition a([] {}); @@ -130,7 +129,7 @@ TEST_CASE("Run until net dies with nullptr") { do { if (reducers->try_dequeue(r)) { m = r(std::move(m)); - m.fireTransitions(reducers, stp, true); + m.fireTransitions(reducers, true); } } while (m.active_transitions_n.size() > 0); @@ -154,7 +153,9 @@ TEST_CASE( } // with this initial marking, all but transition e are possible. Marking m0 = {{"Pa", 1}}; - Model m(net, store, {}, m0, {}, "s"); + auto stp = std::make_shared(1); + + Model m(net, store, {}, m0, {}, "s", stp); auto fireable_transitions = m.getFireableTransitions(); auto find = [&](auto a) { return std::find(fireable_transitions.begin(), fireable_transitions.end(), @@ -184,13 +185,13 @@ TEST_CASE("Step through transitions") { auto stp = std::make_shared(1); // with this initial marking, all but transition e are possible. Marking m0 = {{"Pa", 4}}; - Model m(net, store, {}, m0, {}, "s"); + Model m(net, store, {}, m0, {}, "s", stp); REQUIRE(m.getFireableTransitions().size() == 4); // abcd - m.fire("e", reducers, stp); - m.fire("b", reducers, stp); - m.fire("b", reducers, stp); - m.fire("c", reducers, stp); - m.fire("b", reducers, stp); + m.fire("e", reducers); + m.fire("b", reducers); + m.fire("b", reducers); + m.fire("c", reducers); + m.fire("b", reducers); // there are no reducers ran, so this doesn't update. REQUIRE(m.getActiveTransitions().size() == 4); // there should be no markers left. diff --git a/symmetri/tests/test_priorities.cc b/symmetri/tests/test_priorities.cc index abe5624b..07abf53d 100644 --- a/symmetri/tests/test_priorities.cc +++ b/symmetri/tests/test_priorities.cc @@ -21,8 +21,8 @@ TEST_CASE( Marking m0 = {{"Pa", 1}, {"Pb", 0}, {"Pc", 0}}; auto stp = std::make_shared(1); - auto m = Model(net, store, priority, m0, {}, "s"); - m.fireTransitions(reducers, stp, true); + auto m = Model(net, store, priority, m0, {}, "s", stp); + m.fireTransitions(reducers, true); Reducer r; while (reducers->wait_dequeue_timed(r, std::chrono::milliseconds(1))) { @@ -54,8 +54,8 @@ TEST_CASE("Using nullptr does not queue reducers.") { Marking m0 = {{"Pa", 1}, {"Pb", 0}, {"Pc", 0}}; auto stp = std::make_shared(1); - auto m = Model(net, store, priority, m0, {}, "s"); - m.fireTransitions(reducers, stp, true); + auto m = Model(net, store, priority, m0, {}, "s", stp); + m.fireTransitions(reducers, true); // no reducers needed, as simple transitions are handled within run all. auto prio_t0 = std::find_if(priority.begin(), priority.end(), [](auto e) { return e.first == "t0"; From 936e4f766876df2ea95b16710e380b676ddf7428 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 08:57:54 +0200 Subject: [PATCH 16/27] thread id to class --- symmetri/include/symmetri/symmetri.h | 7 +++++++ symmetri/symmetri.cc | 29 +++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index f8369b20..0848d8c4 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -87,6 +88,10 @@ class PetriNet final { const symmetri::PriorityTable &priority, const std::string &case_id, std::shared_ptr stp); + PetriNet(const PetriNet &o) + : impl(o.impl), + thread_id_(o.thread_id_.load()), + register_functor(o.register_functor) {} /** * @brief register transition gives a handle to manually force a transition to * fire. This is usefull if you want to trigger a transition that has no input @@ -130,6 +135,8 @@ class PetriNet final { std::shared_ptr impl; ///< Pointer to the implementation, all ///< information is stored in Petri + mutable std::atomic> + thread_id_; ///< The id of the thread from which run is called. std::function register_functor; ///< At PetriNet construction this function is ///< created. It can be used to assign a trigger to diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 35fe66ef..fb421243 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -52,8 +52,6 @@ struct Petri { Petri &operator=(Petri &&) noexcept = delete; Model m; ///< The Petri net model - std::atomic> - thread_id_; ///< The id of the thread from which run is called. /** * @brief Construct a new Petri object. Most importantly, it also creates the @@ -71,8 +69,7 @@ struct Petri { Petri(const Net &net, const Marking &m0, std::shared_ptr stp, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id) - : m(net, store, priorities, m0, final_marking, case_id, stp), - thread_id_(std::nullopt) {} + : m(net, store, priorities, m0, final_marking, case_id, stp) {} }; /** @@ -115,7 +112,8 @@ using namespace symmetri; PetriNet::PetriNet(const std::set &files, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, - std::shared_ptr stp) { + std::shared_ptr stp) + : thread_id_(std::nullopt) { const auto [net, m0] = readPnml(files); if (areAllTransitionsInStore(store, net)) { std::tie(impl, register_functor) = @@ -125,8 +123,8 @@ PetriNet::PetriNet(const std::set &files, PetriNet::PetriNet(const std::set &files, const Marking &final_marking, const Store &store, - const std::string &case_id, - std::shared_ptr stp) { + const std::string &case_id, std::shared_ptr stp) + : thread_id_(std::nullopt) { const auto [net, m0, priorities] = readGrml(files); if (areAllTransitionsInStore(store, net)) { std::tie(impl, register_functor) = @@ -137,7 +135,8 @@ PetriNet::PetriNet(const std::set &files, PetriNet::PetriNet(const Net &net, const Marking &m0, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, - std::shared_ptr stp) { + std::shared_ptr stp) + : thread_id_(std::nullopt) { if (areAllTransitionsInStore(store, net)) { std::tie(impl, register_functor) = create(net, m0, final_marking, store, priorities, case_id, stp); @@ -152,11 +151,11 @@ std::function PetriNet::registerTransitionCallback( bool PetriNet::reuseApplication(const std::string &new_case_id) { if (impl) { auto &petri = *impl; - if (petri.thread_id_.load().has_value() || new_case_id == petri.m.case_id) { + if (thread_id_.load().has_value() || new_case_id == petri.m.case_id) { return false; } petri.m.case_id = new_case_id; - petri.thread_id_.store(std::nullopt); + thread_id_.store(std::nullopt); return true; } else { return false; @@ -164,16 +163,16 @@ bool PetriNet::reuseApplication(const std::string &new_case_id) { } symmetri::Result fire(const PetriNet &app) { - if (app.impl == nullptr || app.impl->thread_id_.load()) { + if (app.impl == nullptr || app.thread_id_.load().has_value()) { return {{}, symmetri::State::Error}; } + app.thread_id_.store(symmetri::getThreadId()); auto &petri = *app.impl; auto &m = petri.m; // we are running! auto &active_reducers = petri.m.reducers[petri.m.reducer_selector]; - petri.thread_id_.store(symmetri::getThreadId()); m.event_log.clear(); m.tokens_n = m.initial_tokens; m.state = State::Started; @@ -204,9 +203,8 @@ symmetri::Result fire(const PetriNet &app) { m = f(std::move(m)); } - petri.thread_id_.store(std::nullopt); petri.m.setFreshQueue(); - + app.thread_id_.store(std::nullopt); return {m.event_log, m.state}; }; @@ -254,8 +252,7 @@ void resume(const PetriNet &app) { }; symmetri::Eventlog getLog(const PetriNet &app) { - const auto maybe_thread_id = app.impl->thread_id_.load(); - if (maybe_thread_id && app.impl->thread_id_.load().value() != getThreadId()) { + if (app.thread_id_.load() && app.thread_id_.load().value() != getThreadId()) { const auto &impl = *app.impl; std::promise el; std::future el_getter = el.get_future(); From f03599fea14b9c432ad86c5d34616287dbdd74b9 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 09:07:05 +0200 Subject: [PATCH 17/27] remove the class --- symmetri/model.cc | 18 ++++---- symmetri/model.h | 22 ++++----- symmetri/model_utilities.cc | 4 +- symmetri/symmetri.cc | 74 ++++++++----------------------- symmetri/tests/test_bugs.cc | 2 +- symmetri/tests/test_model.cc | 14 +++--- symmetri/tests/test_priorities.cc | 4 +- 7 files changed, 51 insertions(+), 87 deletions(-) diff --git a/symmetri/model.cc b/symmetri/model.cc index 8322f6bd..32441487 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -81,7 +81,7 @@ std::vector createPriorityLookup( return priority; } -Model::Model(const Net &_net, const Store &store, +Petri::Petri(const Net &_net, const Store &store, const PriorityTable &_priority, const Marking &M0, const Marking &final_marking, const std::string &case_id, std::shared_ptr stp) @@ -104,7 +104,7 @@ Model::Model(const Net &_net, const Store &store, final_marking_n = toTokens(final_marking); } -std::vector Model::toTokens(const Marking &marking) const noexcept { +std::vector Petri::toTokens(const Marking &marking) const noexcept { std::vector tokens; for (const auto &[p, c] : marking) { for (int i = 0; i < c; i++) { @@ -114,7 +114,7 @@ std::vector Model::toTokens(const Marking &marking) const noexcept { return tokens; } -std::vector Model::getFireableTransitions() const { +std::vector Petri::getFireableTransitions() const { auto possible_transition_list_n = possibleTransitions(tokens_n, net.p_to_ts_n, net.priority); std::vector fireable_transitions; @@ -128,7 +128,7 @@ std::vector Model::getFireableTransitions() const { return fireable_transitions; } -bool Model::Fire( +bool Petri::Fire( const size_t t, const std::shared_ptr> &reducers, @@ -162,7 +162,7 @@ bool Model::Fire( } } -bool Model::fire( +bool Petri::fire( const Transition &t, const std::shared_ptr> &reducers, @@ -174,7 +174,7 @@ bool Model::fire( !Fire(std::distance(net.transition.begin(), it), reducers, case_id); } -void Model::fireTransitions( +void Petri::fireTransitions( const std::shared_ptr> &reducers, bool run_all, const std::string &case_id) { @@ -210,7 +210,7 @@ void Model::fireTransitions( return; } -std::vector Model::getMarking() const { +std::vector Petri::getMarking() const { std::vector marking; marking.reserve(tokens_n.size()); std::transform(tokens_n.cbegin(), tokens_n.cend(), @@ -219,7 +219,7 @@ std::vector Model::getMarking() const { return marking; } -std::vector Model::getActiveTransitions() const { +std::vector Petri::getActiveTransitions() const { std::vector transition_list; if (active_transitions_n.size() > 0) { transition_list.reserve(active_transitions_n.size()); @@ -232,7 +232,7 @@ std::vector Model::getActiveTransitions() const { } const std::shared_ptr> - &Model::setFreshQueue() { + &Petri::setFreshQueue() { // increment index to get the already prepared queue. reducer_selector = reducer_selector > 0 ? 0 : 1; // create a new queue for later use at the next index. diff --git a/symmetri/model.h b/symmetri/model.h index 06f2786c..adc20d25 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -54,8 +54,8 @@ gch::small_vector possibleTransitions( */ bool canFire(const SmallVector &pre, const std::vector &tokens); -struct Model; -using Reducer = std::function; +struct Petri; +using Reducer = std::function; /** * @brief Create a Reducer For Transition object @@ -73,14 +73,14 @@ Reducer fireTransition( &reducers); /** - * @brief Model is a different data structure to encode the Petri net. It is + * @brief Petri is a different data structure to encode the Petri net. It is * optimized for calculating the fireable transitions and quick lookups in * ordered vectors. * */ -struct Model { +struct Petri { /** - * @brief Construct a new Model from a multiset description of a Petri + * @brief Construct a new Petri from a multiset description of a Petri * net, a lookup table for the transitions, optional priorities and an initial * marking. A lot of conversion work is done in the constructor, so you should * avoid creating Models during the run-time of a Petri application. @@ -90,15 +90,15 @@ struct Model { * @param priority * @param M0 */ - explicit Model(const Net &net, const Store &store, + explicit Petri(const Net &net, const Store &store, const PriorityTable &priority, const Marking &M0, const Marking &final_marking, const std::string &case_id, std::shared_ptr stp); - ~Model() noexcept = default; - Model(Model const &) = delete; - Model(Model &&) noexcept = delete; - Model &operator=(Model const &) = default; - Model &operator=(Model &&) noexcept = default; + ~Petri() noexcept = default; + Petri(Petri const &) = delete; + Petri(Petri &&) noexcept = delete; + Petri &operator=(Petri const &) = default; + Petri &operator=(Petri &&) noexcept = default; /** * @brief outputs the marking as a vector of tokens; e.g. [1 1 1 0 5] means 3 diff --git a/symmetri/model_utilities.cc b/symmetri/model_utilities.cc index 6c308374..82ba79c2 100644 --- a/symmetri/model_utilities.cc +++ b/symmetri/model_utilities.cc @@ -39,7 +39,7 @@ gch::small_vector possibleTransitions( Reducer createReducerForTransition(size_t t_i, const Eventlog &ev, State result) { - return [=](Model &&model) { + return [=](Petri &&model) { // if it is in the active transition set it means it is finished and we // should process it. auto it = std::find(model.active_transitions_n.begin(), @@ -63,7 +63,7 @@ Reducer fireTransition( const std::shared_ptr> &reducers) { const auto start_time = Clock::now(); - reducers->enqueue([=](Model &&model) { + reducers->enqueue([=](Petri &&model) { model.event_log.push_back( {case_id, transition, State::Started, start_time}); return std::ref(model); diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index fb421243..c136e9c2 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -39,39 +39,6 @@ bool areAllTransitionsInStore(const Store &store, const Net &net) noexcept { }); } -/** - * @brief Petri is the class that holds the implementation of the Petri net. It - * holds pointers to the reducer queue and that thread pool. Calling `fire()` on - * this class will do the *actual* execution of the Petri net. - * - */ -struct Petri { - Petri(Petri const &) = delete; - Petri(Petri &&) noexcept = delete; - Petri &operator=(Petri const &) = delete; - Petri &operator=(Petri &&) noexcept = delete; - - Model m; ///< The Petri net model - - /** - * @brief Construct a new Petri object. Most importantly, it also creates the - * reducer queue and exposes the `run` function to actually run the petri - * net. - * - * @param net - * @param m0 - * @param stp - * @param final_marking - * @param store - * @param priority - * @param case_id - */ - Petri(const Net &net, const Marking &m0, std::shared_ptr stp, - const Marking &final_marking, const Store &store, - const PriorityTable &priorities, const std::string &case_id) - : m(net, store, priorities, m0, final_marking, case_id, stp) {} -}; - /** * @brief A factory function that creates a Petri and a handler that allows to * register triggers to functions. @@ -90,11 +57,11 @@ std::tuple, std::function> create(const Net &net, const Marking &m0, const Marking &final_marking, const Store &store, const PriorityTable &priority, const std::string &case_id, std::shared_ptr stp) { - auto impl = std::make_shared(net, m0, stp, final_marking, store, - priority, case_id); + auto impl = std::make_shared(net, store, priority, m0, final_marking, + case_id, stp); return {impl, [=](const Transition &t) { - const auto &reducer = impl->m.reducers[impl->m.reducer_selector]; - reducer->enqueue([=](Model &&m) { + const auto &reducer = impl->reducers[impl->reducer_selector]; + reducer->enqueue([=](Petri &&m) { const auto t_index = toIndex(m.net.transition, t); m.active_transitions_n.push_back(t_index); reducer->enqueue( @@ -151,10 +118,10 @@ std::function PetriNet::registerTransitionCallback( bool PetriNet::reuseApplication(const std::string &new_case_id) { if (impl) { auto &petri = *impl; - if (thread_id_.load().has_value() || new_case_id == petri.m.case_id) { + if (thread_id_.load().has_value() || new_case_id == petri.case_id) { return false; } - petri.m.case_id = new_case_id; + petri.case_id = new_case_id; thread_id_.store(std::nullopt); return true; } else { @@ -168,17 +135,16 @@ symmetri::Result fire(const PetriNet &app) { } app.thread_id_.store(symmetri::getThreadId()); - auto &petri = *app.impl; - auto &m = petri.m; + auto &m = *app.impl; // we are running! - auto &active_reducers = petri.m.reducers[petri.m.reducer_selector]; + auto &active_reducers = m.reducers[m.reducer_selector]; m.event_log.clear(); m.tokens_n = m.initial_tokens; m.state = State::Started; symmetri::Reducer f; // start! - m.fireTransitions(active_reducers, true, petri.m.case_id); + m.fireTransitions(active_reducers, true, m.case_id); while ((m.state == State::Started || m.state == State::Paused) && active_reducers->wait_dequeue_timed(f, -1)) { do { @@ -191,7 +157,7 @@ symmetri::Result fire(const PetriNet &app) { if (m.state == State::Started) { // we're firing - m.fireTransitions(active_reducers, true, petri.m.case_id); + m.fireTransitions(active_reducers, true, m.case_id); // if there's nothing to fire; we deadlocked m.state = m.active_transitions_n.size() == 0 ? State::Deadlock : m.state; } @@ -203,15 +169,14 @@ symmetri::Result fire(const PetriNet &app) { m = f(std::move(m)); } - petri.m.setFreshQueue(); + m.setFreshQueue(); app.thread_id_.store(std::nullopt); return {m.event_log, m.state}; }; symmetri::Result cancel(const PetriNet &app) { - const auto &impl = *app.impl; - impl.m.reducers[impl.m.reducer_selector]->enqueue( - [=](symmetri::Model &&model) { + app.impl->reducers[app.impl->reducer_selector]->enqueue( + [=](symmetri::Petri &&model) { model.state = symmetri::State::UserExit; // populate that eventlog with child eventlog and possible cancelations. for (const auto transition_index : model.active_transitions_n) { @@ -230,8 +195,8 @@ symmetri::Result cancel(const PetriNet &app) { } void pause(const PetriNet &app) { - app.impl->m.reducers[app.impl->m.reducer_selector]->enqueue( - [](symmetri::Model &&model) { + app.impl->reducers[app.impl->reducer_selector]->enqueue( + [](symmetri::Petri &&model) { model.state = State::Paused; for (const auto transition_index : model.active_transitions_n) { pause(model.net.store.at(transition_index)); @@ -241,8 +206,8 @@ void pause(const PetriNet &app) { }; void resume(const PetriNet &app) { - app.impl->m.reducers[app.impl->m.reducer_selector]->enqueue( - [](symmetri::Model &&model) { + app.impl->reducers[app.impl->reducer_selector]->enqueue( + [](symmetri::Petri &&model) { model.state = State::Started; for (const auto transition_index : model.active_transitions_n) { resume(model.net.store.at(transition_index)); @@ -253,10 +218,9 @@ void resume(const PetriNet &app) { symmetri::Eventlog getLog(const PetriNet &app) { if (app.thread_id_.load() && app.thread_id_.load().value() != getThreadId()) { - const auto &impl = *app.impl; std::promise el; std::future el_getter = el.get_future(); - impl.m.reducers[impl.m.reducer_selector]->enqueue([&](Model &&model) { + app.impl->reducers[app.impl->reducer_selector]->enqueue([&](Petri &&model) { auto eventlog = model.event_log; // get event log from parent nets: for (size_t pt_idx : model.active_transitions_n) { @@ -270,6 +234,6 @@ symmetri::Eventlog getLog(const PetriNet &app) { }); return el_getter.get(); } else { - return app.impl->m.event_log; + return app.impl->event_log; } } diff --git a/symmetri/tests/test_bugs.cc b/symmetri/tests/test_bugs.cc index 57b0ba60..2aa6fea3 100644 --- a/symmetri/tests/test_bugs.cc +++ b/symmetri/tests/test_bugs.cc @@ -34,7 +34,7 @@ std::tuple testNet() { TEST_CASE("Firing the same transition before it can complete should work") { auto [net, store, priority, m0] = testNet(); auto stp = std::make_shared(2); - Model m(net, store, priority, m0, {}, "s", stp); + Petri m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); REQUIRE(m.active_transitions_n.empty()); diff --git a/symmetri/tests/test_model.cc b/symmetri/tests/test_model.cc index 7c87541d..623b915b 100644 --- a/symmetri/tests/test_model.cc +++ b/symmetri/tests/test_model.cc @@ -50,7 +50,7 @@ TEST_CASE("Run one transition iteration in a petri net") { auto [net, store, priority, m0] = testNet(); auto stp = std::make_shared(1); - Model m(net, store, priority, m0, {}, "s", stp); + Petri m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); // t0 is enabled. @@ -89,14 +89,14 @@ TEST_CASE("Run until net dies") { auto [net, store, priority, m0] = testNet(); auto stp = std::make_shared(1); - Model m(net, store, priority, m0, {}, "s", stp); + Petri m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); Reducer r; PolyTransition a([] {}); // we need to enqueue one 'no-operation' to start the live net. - reducers->enqueue([](Model&& m) { return std::ref(m); }); + reducers->enqueue([](Petri&& m) { return std::ref(m); }); do { if (reducers->try_dequeue(r)) { m = r(std::move(m)); @@ -118,14 +118,14 @@ TEST_CASE("Run until net dies with nullptr") { auto [net, store, priority, m0] = testNet(); store = {{"t0", DirectMutation{}}, {"t1", DirectMutation{}}}; auto stp = std::make_shared(1); - Model m(net, store, priority, m0, {}, "s", stp); + Petri m(net, store, priority, m0, {}, "s", stp); auto reducers = std::make_shared>(4); Reducer r; PolyTransition a([] {}); // we need to enqueue one 'no-operation' to start the live net. - reducers->enqueue([](Model&& m) { return std::ref(m); }); + reducers->enqueue([](Petri&& m) { return std::ref(m); }); do { if (reducers->try_dequeue(r)) { m = r(std::move(m)); @@ -155,7 +155,7 @@ TEST_CASE( Marking m0 = {{"Pa", 1}}; auto stp = std::make_shared(1); - Model m(net, store, {}, m0, {}, "s", stp); + Petri m(net, store, {}, m0, {}, "s", stp); auto fireable_transitions = m.getFireableTransitions(); auto find = [&](auto a) { return std::find(fireable_transitions.begin(), fireable_transitions.end(), @@ -185,7 +185,7 @@ TEST_CASE("Step through transitions") { auto stp = std::make_shared(1); // with this initial marking, all but transition e are possible. Marking m0 = {{"Pa", 4}}; - Model m(net, store, {}, m0, {}, "s", stp); + Petri m(net, store, {}, m0, {}, "s", stp); REQUIRE(m.getFireableTransitions().size() == 4); // abcd m.fire("e", reducers); m.fire("b", reducers); diff --git a/symmetri/tests/test_priorities.cc b/symmetri/tests/test_priorities.cc index 07abf53d..0a4d0a9b 100644 --- a/symmetri/tests/test_priorities.cc +++ b/symmetri/tests/test_priorities.cc @@ -21,7 +21,7 @@ TEST_CASE( Marking m0 = {{"Pa", 1}, {"Pb", 0}, {"Pc", 0}}; auto stp = std::make_shared(1); - auto m = Model(net, store, priority, m0, {}, "s", stp); + auto m = Petri(net, store, priority, m0, {}, "s", stp); m.fireTransitions(reducers, true); Reducer r; @@ -54,7 +54,7 @@ TEST_CASE("Using nullptr does not queue reducers.") { Marking m0 = {{"Pa", 1}, {"Pb", 0}, {"Pc", 0}}; auto stp = std::make_shared(1); - auto m = Model(net, store, priority, m0, {}, "s", stp); + auto m = Petri(net, store, priority, m0, {}, "s", stp); m.fireTransitions(reducers, true); // no reducers needed, as simple transitions are handled within run all. auto prio_t0 = std::find_if(priority.begin(), priority.end(), [](auto e) { From ec63a60d876e9ebfeabb52bdb21bcd0c28b80126 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 19:15:08 +0200 Subject: [PATCH 18/27] simpler reducer --- symmetri/include/symmetri/symmetri.h | 18 ----------- symmetri/model.cc | 3 +- symmetri/model.h | 9 ++++-- symmetri/model_utilities.cc | 6 ++-- symmetri/symmetri.cc | 47 ++++++++++++---------------- symmetri/tests/test_bugs.cc | 6 ++-- symmetri/tests/test_model.cc | 14 ++++----- symmetri/tests/test_priorities.cc | 2 +- 8 files changed, 41 insertions(+), 64 deletions(-) diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index 0848d8c4..466b62d5 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -88,10 +88,6 @@ class PetriNet final { const symmetri::PriorityTable &priority, const std::string &case_id, std::shared_ptr stp); - PetriNet(const PetriNet &o) - : impl(o.impl), - thread_id_(o.thread_id_.load()), - register_functor(o.register_functor) {} /** * @brief register transition gives a handle to manually force a transition to * fire. This is usefull if you want to trigger a transition that has no input @@ -105,18 +101,6 @@ class PetriNet final { std::function registerTransitionCallback( const std::string &transition) const noexcept; - /** - * @brief Get the State, represented by a vector of *active* transitions (who - * can still produce reducers and hence marking mutations) and the *current - * marking*. If the Petri net is running, this call is blocking as it is - * executed on the Petri net execution loop. Otherwise it directly returns the - * state. - * - * @return std::pair, std::vector> - */ - std::pair, std::vector> - getState() const noexcept; - /** * @brief reuseApplication resets the application such that the same net can * be used again after an cancel call. You do need to supply a new case_id @@ -135,8 +119,6 @@ class PetriNet final { std::shared_ptr impl; ///< Pointer to the implementation, all ///< information is stored in Petri - mutable std::atomic> - thread_id_; ///< The id of the thread from which run is called. std::function register_functor; ///< At PetriNet construction this function is ///< created. It can be used to assign a trigger to diff --git a/symmetri/model.cc b/symmetri/model.cc index 32441487..4c137883 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -92,7 +92,8 @@ Petri::Petri(const Net &_net, const Store &store, {std::make_shared>(32), std::make_shared>(32)}), reducer_selector(0), - pool(stp) { + pool(stp), + thread_id_(std::nullopt) { event_log.reserve(1000); std::tie(net.transition, net.place, net.store) = convert(_net, store); std::tie(net.input_n, net.output_n) = populateIoLookups(_net, net.place); diff --git a/symmetri/model.h b/symmetri/model.h index adc20d25..882e142c 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "small_vector.hpp" #include "symmetri/polytransition.h" @@ -55,7 +56,7 @@ gch::small_vector possibleTransitions( bool canFire(const SmallVector &pre, const std::vector &tokens); struct Petri; -using Reducer = std::function; +using Reducer = std::function; /** * @brief Create a Reducer For Transition object @@ -97,8 +98,8 @@ struct Petri { ~Petri() noexcept = default; Petri(Petri const &) = delete; Petri(Petri &&) noexcept = delete; - Petri &operator=(Petri const &) = default; - Petri &operator=(Petri &&) noexcept = default; + Petri &operator=(Petri const &) = delete; + Petri &operator=(Petri &&) noexcept = delete; /** * @brief outputs the marking as a vector of tokens; e.g. [1 1 1 0 5] means 3 @@ -203,6 +204,8 @@ struct Petri { reducers; unsigned int reducer_selector; std::shared_ptr pool; + std::atomic> + thread_id_; ///< The id of the thread from which run is called. const std::shared_ptr> &setFreshQueue(); diff --git a/symmetri/model_utilities.cc b/symmetri/model_utilities.cc index 82ba79c2..ba60ca01 100644 --- a/symmetri/model_utilities.cc +++ b/symmetri/model_utilities.cc @@ -39,7 +39,7 @@ gch::small_vector possibleTransitions( Reducer createReducerForTransition(size_t t_i, const Eventlog &ev, State result) { - return [=](Petri &&model) { + return [=](Petri &model) { // if it is in the active transition set it means it is finished and we // should process it. auto it = std::find(model.active_transitions_n.begin(), @@ -53,7 +53,6 @@ Reducer createReducerForTransition(size_t t_i, const Eventlog &ev, } model.event_log.insert(model.event_log.end(), ev.begin(), ev.end()); }; - return std::ref(model); }; } @@ -63,10 +62,9 @@ Reducer fireTransition( const std::shared_ptr> &reducers) { const auto start_time = Clock::now(); - reducers->enqueue([=](Petri &&model) { + reducers->enqueue([=](Petri &model) { model.event_log.push_back( {case_id, transition, State::Started, start_time}); - return std::ref(model); }); auto [ev, res] = fire(task); diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index c136e9c2..7b6be795 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "model.h" @@ -61,13 +60,12 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, case_id, stp); return {impl, [=](const Transition &t) { const auto &reducer = impl->reducers[impl->reducer_selector]; - reducer->enqueue([=](Petri &&m) { + reducer->enqueue([=](Petri &m) { const auto t_index = toIndex(m.net.transition, t); m.active_transitions_n.push_back(t_index); reducer->enqueue( fireTransition(t_index, m.net.transition[t_index], m.net.store[t_index], m.case_id, reducer)); - return std::ref(m); }); }}; } @@ -79,8 +77,7 @@ using namespace symmetri; PetriNet::PetriNet(const std::set &files, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, - std::shared_ptr stp) - : thread_id_(std::nullopt) { + std::shared_ptr stp) { const auto [net, m0] = readPnml(files); if (areAllTransitionsInStore(store, net)) { std::tie(impl, register_functor) = @@ -90,8 +87,8 @@ PetriNet::PetriNet(const std::set &files, PetriNet::PetriNet(const std::set &files, const Marking &final_marking, const Store &store, - const std::string &case_id, std::shared_ptr stp) - : thread_id_(std::nullopt) { + const std::string &case_id, + std::shared_ptr stp) { const auto [net, m0, priorities] = readGrml(files); if (areAllTransitionsInStore(store, net)) { std::tie(impl, register_functor) = @@ -102,8 +99,7 @@ PetriNet::PetriNet(const std::set &files, PetriNet::PetriNet(const Net &net, const Marking &m0, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, - std::shared_ptr stp) - : thread_id_(std::nullopt) { + std::shared_ptr stp) { if (areAllTransitionsInStore(store, net)) { std::tie(impl, register_functor) = create(net, m0, final_marking, store, priorities, case_id, stp); @@ -117,12 +113,10 @@ std::function PetriNet::registerTransitionCallback( bool PetriNet::reuseApplication(const std::string &new_case_id) { if (impl) { - auto &petri = *impl; - if (thread_id_.load().has_value() || new_case_id == petri.case_id) { + if (impl->thread_id_.load().has_value() || new_case_id == impl->case_id) { return false; } - petri.case_id = new_case_id; - thread_id_.store(std::nullopt); + impl->case_id = new_case_id; return true; } else { return false; @@ -130,12 +124,12 @@ bool PetriNet::reuseApplication(const std::string &new_case_id) { } symmetri::Result fire(const PetriNet &app) { - if (app.impl == nullptr || app.thread_id_.load().has_value()) { + if (app.impl == nullptr || app.impl->thread_id_.load().has_value()) { return {{}, symmetri::State::Error}; } - app.thread_id_.store(symmetri::getThreadId()); auto &m = *app.impl; + m.thread_id_.store(symmetri::getThreadId()); // we are running! auto &active_reducers = m.reducers[m.reducer_selector]; @@ -148,7 +142,8 @@ symmetri::Result fire(const PetriNet &app) { while ((m.state == State::Started || m.state == State::Paused) && active_reducers->wait_dequeue_timed(f, -1)) { do { - m = f(std::move(m)); + // m = f(std::move(m)); + f(m); } while (active_reducers->try_dequeue(f)); m.state = symmetri::MarkingReached(m.tokens_n, m.final_marking_n) @@ -166,17 +161,18 @@ symmetri::Result fire(const PetriNet &app) { // empty reducers m.active_transitions_n.clear(); while (active_reducers->try_dequeue(f)) { - m = f(std::move(m)); + // m = f(std::move(m)); + f(m); } m.setFreshQueue(); - app.thread_id_.store(std::nullopt); + m.thread_id_.store(std::nullopt); return {m.event_log, m.state}; }; symmetri::Result cancel(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( - [=](symmetri::Petri &&model) { + [=](symmetri::Petri &model) { model.state = symmetri::State::UserExit; // populate that eventlog with child eventlog and possible cancelations. for (const auto transition_index : model.active_transitions_n) { @@ -188,7 +184,6 @@ symmetri::Result cancel(const PetriNet &app) { model.net.transition[transition_index], state, symmetri::Clock::now()}); } - return std::ref(model); }); return {getLog(app), symmetri::State::UserExit}; @@ -196,31 +191,30 @@ symmetri::Result cancel(const PetriNet &app) { void pause(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( - [](symmetri::Petri &&model) { + [](symmetri::Petri &model) { model.state = State::Paused; for (const auto transition_index : model.active_transitions_n) { pause(model.net.store.at(transition_index)); } - return std::ref(model); }); }; void resume(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( - [](symmetri::Petri &&model) { + [](symmetri::Petri &model) { model.state = State::Started; for (const auto transition_index : model.active_transitions_n) { resume(model.net.store.at(transition_index)); } - return std::ref(model); }); }; symmetri::Eventlog getLog(const PetriNet &app) { - if (app.thread_id_.load() && app.thread_id_.load().value() != getThreadId()) { + if (app.impl->thread_id_.load() && + app.impl->thread_id_.load().value() != getThreadId()) { std::promise el; std::future el_getter = el.get_future(); - app.impl->reducers[app.impl->reducer_selector]->enqueue([&](Petri &&model) { + app.impl->reducers[app.impl->reducer_selector]->enqueue([&](Petri &model) { auto eventlog = model.event_log; // get event log from parent nets: for (size_t pt_idx : model.active_transitions_n) { @@ -230,7 +224,6 @@ symmetri::Eventlog getLog(const PetriNet &app) { } } el.set_value(std::move(eventlog)); - return std::ref(model); }); return el_getter.get(); } else { diff --git a/symmetri/tests/test_bugs.cc b/symmetri/tests/test_bugs.cc index 2aa6fea3..10942ec5 100644 --- a/symmetri/tests/test_bugs.cc +++ b/symmetri/tests/test_bugs.cc @@ -46,7 +46,7 @@ TEST_CASE("Firing the same transition before it can complete should work") { Reducer r; while (reducers->wait_dequeue_timed(r, std::chrono::milliseconds(250))) { - m = r(std::move(m)); + r(m); } { @@ -57,7 +57,7 @@ TEST_CASE("Firing the same transition before it can complete should work") { cv.notify_one(); reducers->wait_dequeue_timed(r, std::chrono::milliseconds(250)); - m = r(std::move(m)); + r(m); REQUIRE(MarkingEquality(m.getMarking(), {"Pb"})); // offending test, but fixed :-) @@ -69,7 +69,7 @@ TEST_CASE("Firing the same transition before it can complete should work") { } cv.notify_one(); reducers->wait_dequeue_timed(r, std::chrono::milliseconds(250)); - m = r(std::move(m)); + r(m); REQUIRE(MarkingEquality(m.getMarking(), {"Pb", "Pb"})); diff --git a/symmetri/tests/test_model.cc b/symmetri/tests/test_model.cc index 623b915b..87602b0b 100644 --- a/symmetri/tests/test_model.cc +++ b/symmetri/tests/test_model.cc @@ -76,8 +76,8 @@ TEST_CASE("Run one transition iteration in a petri net") { std::vector{"t0", "t0"}); // process the reducers - m = r1(std::move(m)); - m = r2(std::move(m)); + r1(m); + r2(m); // and now the post-conditions are processed: REQUIRE(m.active_transitions_n.empty()); REQUIRE(MarkingEquality( @@ -96,10 +96,10 @@ TEST_CASE("Run until net dies") { Reducer r; PolyTransition a([] {}); // we need to enqueue one 'no-operation' to start the live net. - reducers->enqueue([](Petri&& m) { return std::ref(m); }); + reducers->enqueue([](Petri&) {}); do { if (reducers->try_dequeue(r)) { - m = r(std::move(m)); + r(m); m.fireTransitions(reducers, true); } } while (m.active_transitions_n.size() > 0); @@ -125,10 +125,10 @@ TEST_CASE("Run until net dies with nullptr") { Reducer r; PolyTransition a([] {}); // we need to enqueue one 'no-operation' to start the live net. - reducers->enqueue([](Petri&& m) { return std::ref(m); }); + reducers->enqueue([](Petri&) {}); do { if (reducers->try_dequeue(r)) { - m = r(std::move(m)); + r(m); m.fireTransitions(reducers, true); } } while (m.active_transitions_n.size() > 0); @@ -203,7 +203,7 @@ TEST_CASE("Step through transitions") { while (j < 2 * 4 && reducers->wait_dequeue_timed(r, std::chrono::seconds(1))) { j++; - m = r(std::move(m)); + r(m); } // reducers update, there should be active transitions left. REQUIRE(m.getActiveTransitions().size() == 0); diff --git a/symmetri/tests/test_priorities.cc b/symmetri/tests/test_priorities.cc index 0a4d0a9b..a9cfb7d6 100644 --- a/symmetri/tests/test_priorities.cc +++ b/symmetri/tests/test_priorities.cc @@ -26,7 +26,7 @@ TEST_CASE( Reducer r; while (reducers->wait_dequeue_timed(r, std::chrono::milliseconds(1))) { - m = r(std::move(m)); + r(m); } auto prio_t0 = std::find_if(priority.begin(), priority.end(), [](auto e) { From 3e43b4a84a7a95034a25159d1de79b139cb35d58 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 19:23:09 +0200 Subject: [PATCH 19/27] remove impl nullptr checks --- symmetri/symmetri.cc | 13 +++++-------- symmetri/tests/test_symmetri.cc | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 7b6be795..6ab473fb 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -112,19 +112,15 @@ std::function PetriNet::registerTransitionCallback( } bool PetriNet::reuseApplication(const std::string &new_case_id) { - if (impl) { - if (impl->thread_id_.load().has_value() || new_case_id == impl->case_id) { - return false; - } + if (!impl->thread_id_.load().has_value() && new_case_id != impl->case_id) { impl->case_id = new_case_id; return true; - } else { - return false; } + return false; } symmetri::Result fire(const PetriNet &app) { - if (app.impl == nullptr || app.impl->thread_id_.load().has_value()) { + if (app.impl->thread_id_.load().has_value()) { return {{}, symmetri::State::Error}; } @@ -174,7 +170,8 @@ symmetri::Result cancel(const PetriNet &app) { app.impl->reducers[app.impl->reducer_selector]->enqueue( [=](symmetri::Petri &model) { model.state = symmetri::State::UserExit; - // populate that eventlog with child eventlog and possible cancelations. + // populate that eventlog with child eventlog and possible + // cancelations. for (const auto transition_index : model.active_transitions_n) { auto [el, state] = cancel(model.net.store.at(transition_index)); if (!el.empty()) { diff --git a/symmetri/tests/test_symmetri.cc b/symmetri/tests/test_symmetri.cc index 2611b155..0293c5cf 100644 --- a/symmetri/tests/test_symmetri.cc +++ b/symmetri/tests/test_symmetri.cc @@ -50,7 +50,6 @@ TEST_CASE("Create a using the net constructor with end condition.") { TEST_CASE("Create a using pnml constructor.") { const std::string pnml_file = std::filesystem::current_path().append( "../../../symmetri/tests/assets/PT1.pnml"); - { auto stp = std::make_shared(1); // This store is not appropriate for this net, From 72706073f1d0742de1bfbd6cd8b145d05b48df85 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 19:45:22 +0200 Subject: [PATCH 20/27] fail harder if the store is incomplete --- symmetri/symmetri.cc | 37 ++++++++++++++------------------- symmetri/tests/test_symmetri.cc | 9 ++------ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 6ab473fb..b32889b8 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "model.h" @@ -22,20 +23,17 @@ unsigned int getThreadId() { std::hash()(std::this_thread::get_id())); } -using namespace moodycamel; - -bool areAllTransitionsInStore(const Store &store, const Net &net) noexcept { - return std::all_of(net.cbegin(), net.cend(), [&store](const auto &p) { - const auto &t = std::get<0>(p); +void areAllTransitionsInStore(const Store &store, const Net &net) { + for (const auto &pair : net) { bool store_has_transition = std::find_if(store.begin(), store.end(), [&](const auto &e) { - return e.first == t; + return e.first == pair.first; }) != store.end(); if (!store_has_transition) { - // error seriously here or default? + std::string err = "transition " + pair.first + " is not in store"; + throw std::runtime_error(err); } - return store_has_transition; - }); + } } /** @@ -79,10 +77,9 @@ PetriNet::PetriNet(const std::set &files, const PriorityTable &priorities, const std::string &case_id, std::shared_ptr stp) { const auto [net, m0] = readPnml(files); - if (areAllTransitionsInStore(store, net)) { - std::tie(impl, register_functor) = - create(net, m0, final_marking, store, priorities, case_id, stp); - } + areAllTransitionsInStore(store, net); + std::tie(impl, register_functor) = + create(net, m0, final_marking, store, priorities, case_id, stp); } PetriNet::PetriNet(const std::set &files, @@ -90,20 +87,18 @@ PetriNet::PetriNet(const std::set &files, const std::string &case_id, std::shared_ptr stp) { const auto [net, m0, priorities] = readGrml(files); - if (areAllTransitionsInStore(store, net)) { - std::tie(impl, register_functor) = - create(net, m0, final_marking, store, priorities, case_id, stp); - } + areAllTransitionsInStore(store, net); + std::tie(impl, register_functor) = + create(net, m0, final_marking, store, priorities, case_id, stp); } PetriNet::PetriNet(const Net &net, const Marking &m0, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, std::shared_ptr stp) { - if (areAllTransitionsInStore(store, net)) { - std::tie(impl, register_functor) = - create(net, m0, final_marking, store, priorities, case_id, stp); - } + areAllTransitionsInStore(store, net); + std::tie(impl, register_functor) = + create(net, m0, final_marking, store, priorities, case_id, stp); } std::function PetriNet::registerTransitionCallback( diff --git a/symmetri/tests/test_symmetri.cc b/symmetri/tests/test_symmetri.cc index 0293c5cf..a51258ad 100644 --- a/symmetri/tests/test_symmetri.cc +++ b/symmetri/tests/test_symmetri.cc @@ -55,13 +55,8 @@ TEST_CASE("Create a using pnml constructor.") { // This store is not appropriate for this net, Store store = {{"wrong_id", &t0}}; PriorityTable priority; - PetriNet app({pnml_file}, {}, store, priority, "fail", stp); - // however, we can try running it, - auto [ev, res] = fire(app); - - // but the result is an error. - REQUIRE(res == State::Error); - REQUIRE(ev.empty()); + // it should throw + REQUIRE_THROWS(PetriNet({pnml_file}, {}, store, priority, "fail", stp)); } { From 2a86de9c6728be7736933819fcf1e779585f4a59 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 19:59:22 +0200 Subject: [PATCH 21/27] and undo the double queues --- symmetri/model.cc | 17 ++------- symmetri/model.h | 7 +--- symmetri/symmetri.cc | 83 ++++++++++++++++++++------------------------ 3 files changed, 41 insertions(+), 66 deletions(-) diff --git a/symmetri/model.cc b/symmetri/model.cc index 4c137883..c82bacbb 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -88,10 +88,8 @@ Petri::Petri(const Net &_net, const Store &store, : event_log({}), state(State::Scheduled), case_id(case_id), - reducers( - {std::make_shared>(32), - std::make_shared>(32)}), - reducer_selector(0), + active_reducers( + std::make_shared>(32)), pool(stp), thread_id_(std::nullopt) { event_log.reserve(1000); @@ -232,15 +230,4 @@ std::vector Petri::getActiveTransitions() const { return transition_list; } -const std::shared_ptr> - &Petri::setFreshQueue() { - // increment index to get the already prepared queue. - reducer_selector = reducer_selector > 0 ? 0 : 1; - // create a new queue for later use at the next index. - pool->push([reducer = reducers[reducer_selector > 0 ? 0 : 1]]() mutable { - reducer.reset(new moodycamel::BlockingConcurrentQueue{32}); - }); - return reducers[reducer_selector]; -} - } // namespace symmetri diff --git a/symmetri/model.h b/symmetri/model.h index 882e142c..ba8b8bf9 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -200,16 +200,11 @@ struct Petri { State state; std::string case_id; - std::array>, 2> - reducers; - unsigned int reducer_selector; + std::shared_ptr> active_reducers; std::shared_ptr pool; std::atomic> thread_id_; ///< The id of the thread from which run is called. - const std::shared_ptr> - &setFreshQueue(); - private: /** * @brief fires a transition. diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index b32889b8..184e4707 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -57,13 +57,12 @@ create(const Net &net, const Marking &m0, const Marking &final_marking, auto impl = std::make_shared(net, store, priority, m0, final_marking, case_id, stp); return {impl, [=](const Transition &t) { - const auto &reducer = impl->reducers[impl->reducer_selector]; - reducer->enqueue([=](Petri &m) { + impl->active_reducers->enqueue([=](Petri &m) { const auto t_index = toIndex(m.net.transition, t); m.active_transitions_n.push_back(t_index); - reducer->enqueue( - fireTransition(t_index, m.net.transition[t_index], - m.net.store[t_index], m.case_id, reducer)); + m.active_reducers->enqueue(fireTransition( + t_index, m.net.transition[t_index], m.net.store[t_index], + m.case_id, m.active_reducers)); }); }}; } @@ -118,24 +117,23 @@ symmetri::Result fire(const PetriNet &app) { if (app.impl->thread_id_.load().has_value()) { return {{}, symmetri::State::Error}; } - auto &m = *app.impl; m.thread_id_.store(symmetri::getThreadId()); // we are running! - auto &active_reducers = m.reducers[m.reducer_selector]; m.event_log.clear(); m.tokens_n = m.initial_tokens; m.state = State::Started; symmetri::Reducer f; + while (m.active_reducers->try_dequeue(f)) { /* get rid of old reducers */ + } // start! - m.fireTransitions(active_reducers, true, m.case_id); + m.fireTransitions(m.active_reducers, true, m.case_id); while ((m.state == State::Started || m.state == State::Paused) && - active_reducers->wait_dequeue_timed(f, -1)) { + m.active_reducers->wait_dequeue_timed(f, -1)) { do { - // m = f(std::move(m)); f(m); - } while (active_reducers->try_dequeue(f)); + } while (m.active_reducers->try_dequeue(f)); m.state = symmetri::MarkingReached(m.tokens_n, m.final_marking_n) ? State::Completed @@ -143,7 +141,7 @@ symmetri::Result fire(const PetriNet &app) { if (m.state == State::Started) { // we're firing - m.fireTransitions(active_reducers, true, m.case_id); + m.fireTransitions(m.active_reducers, true, m.case_id); // if there's nothing to fire; we deadlocked m.state = m.active_transitions_n.size() == 0 ? State::Deadlock : m.state; } @@ -151,54 +149,49 @@ symmetri::Result fire(const PetriNet &app) { // empty reducers m.active_transitions_n.clear(); - while (active_reducers->try_dequeue(f)) { - // m = f(std::move(m)); + while (m.active_reducers->try_dequeue(f)) { f(m); } - m.setFreshQueue(); m.thread_id_.store(std::nullopt); return {m.event_log, m.state}; }; symmetri::Result cancel(const PetriNet &app) { - app.impl->reducers[app.impl->reducer_selector]->enqueue( - [=](symmetri::Petri &model) { - model.state = symmetri::State::UserExit; - // populate that eventlog with child eventlog and possible - // cancelations. - for (const auto transition_index : model.active_transitions_n) { - auto [el, state] = cancel(model.net.store.at(transition_index)); - if (!el.empty()) { - model.event_log.insert(model.event_log.end(), el.begin(), el.end()); - } - model.event_log.push_back({model.case_id, - model.net.transition[transition_index], - state, symmetri::Clock::now()}); - } - }); + app.impl->active_reducers->enqueue([=](symmetri::Petri &model) { + model.state = symmetri::State::UserExit; + // populate that eventlog with child eventlog and possible + // cancelations. + for (const auto transition_index : model.active_transitions_n) { + auto [el, state] = cancel(model.net.store.at(transition_index)); + if (!el.empty()) { + model.event_log.insert(model.event_log.end(), el.begin(), el.end()); + } + model.event_log.push_back({model.case_id, + model.net.transition[transition_index], state, + symmetri::Clock::now()}); + } + }); return {getLog(app), symmetri::State::UserExit}; } void pause(const PetriNet &app) { - app.impl->reducers[app.impl->reducer_selector]->enqueue( - [](symmetri::Petri &model) { - model.state = State::Paused; - for (const auto transition_index : model.active_transitions_n) { - pause(model.net.store.at(transition_index)); - } - }); + app.impl->active_reducers->enqueue([](symmetri::Petri &model) { + model.state = State::Paused; + for (const auto transition_index : model.active_transitions_n) { + pause(model.net.store.at(transition_index)); + } + }); }; void resume(const PetriNet &app) { - app.impl->reducers[app.impl->reducer_selector]->enqueue( - [](symmetri::Petri &model) { - model.state = State::Started; - for (const auto transition_index : model.active_transitions_n) { - resume(model.net.store.at(transition_index)); - } - }); + app.impl->active_reducers->enqueue([](symmetri::Petri &model) { + model.state = State::Started; + for (const auto transition_index : model.active_transitions_n) { + resume(model.net.store.at(transition_index)); + } + }); }; symmetri::Eventlog getLog(const PetriNet &app) { @@ -206,7 +199,7 @@ symmetri::Eventlog getLog(const PetriNet &app) { app.impl->thread_id_.load().value() != getThreadId()) { std::promise el; std::future el_getter = el.get_future(); - app.impl->reducers[app.impl->reducer_selector]->enqueue([&](Petri &model) { + app.impl->active_reducers->enqueue([&](Petri &model) { auto eventlog = model.event_log; // get event log from parent nets: for (size_t pt_idx : model.active_transitions_n) { From 93a2e2c7585c09a89c85b97cb3294ef957d36649 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 20:17:38 +0200 Subject: [PATCH 22/27] small cleanup --- symmetri/include/symmetri/symmetri.h | 7 ---- symmetri/model.cc | 4 +- symmetri/model.h | 5 +-- symmetri/symmetri.cc | 55 ++++++++-------------------- 4 files changed, 20 insertions(+), 51 deletions(-) diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index 466b62d5..b7215eda 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include @@ -119,9 +117,4 @@ class PetriNet final { std::shared_ptr impl; ///< Pointer to the implementation, all ///< information is stored in Petri - std::function - register_functor; ///< At PetriNet construction this function is - ///< created. It can be used to assign a trigger to - ///< transitions - allowing the user to invoke a - ///< transition without meeting the pre-conditions. }; diff --git a/symmetri/model.cc b/symmetri/model.cc index c82bacbb..5443b132 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -88,10 +88,10 @@ Petri::Petri(const Net &_net, const Store &store, : event_log({}), state(State::Scheduled), case_id(case_id), + thread_id_(std::nullopt), active_reducers( std::make_shared>(32)), - pool(stp), - thread_id_(std::nullopt) { + pool(stp) { event_log.reserve(1000); std::tie(net.transition, net.place, net.store) = convert(_net, store); std::tie(net.input_n, net.output_n) = populateIoLookups(_net, net.place); diff --git a/symmetri/model.h b/symmetri/model.h index ba8b8bf9..a57ef0a9 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -199,11 +198,11 @@ struct Petri { Eventlog event_log; ///< The most actual event_log State state; std::string case_id; + std::atomic> + thread_id_; ///< The id of the thread from which run is called. std::shared_ptr> active_reducers; std::shared_ptr pool; - std::atomic> - thread_id_; ///< The id of the thread from which run is called. private: /** diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 184e4707..45389d2d 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -36,37 +36,6 @@ void areAllTransitionsInStore(const Store &store, const Net &net) { } } -/** - * @brief A factory function that creates a Petri and a handler that allows to - * register triggers to functions. - * - * @param net - * @param m0 - * @param final_marking - * @param store - * @param priority - * @param case_id - * @param stp - * @return std::tuple, std::function> - */ -std::tuple, std::function> -create(const Net &net, const Marking &m0, const Marking &final_marking, - const Store &store, const PriorityTable &priority, - const std::string &case_id, std::shared_ptr stp) { - auto impl = std::make_shared(net, store, priority, m0, final_marking, - case_id, stp); - return {impl, [=](const Transition &t) { - impl->active_reducers->enqueue([=](Petri &m) { - const auto t_index = toIndex(m.net.transition, t); - m.active_transitions_n.push_back(t_index); - m.active_reducers->enqueue(fireTransition( - t_index, m.net.transition[t_index], m.net.store[t_index], - m.case_id, m.active_reducers)); - }); - }}; -} - } // namespace symmetri using namespace symmetri; @@ -77,8 +46,8 @@ PetriNet::PetriNet(const std::set &files, std::shared_ptr stp) { const auto [net, m0] = readPnml(files); areAllTransitionsInStore(store, net); - std::tie(impl, register_functor) = - create(net, m0, final_marking, store, priorities, case_id, stp); + impl = std::make_shared(net, store, priorities, m0, final_marking, + case_id, stp); } PetriNet::PetriNet(const std::set &files, @@ -87,22 +56,30 @@ PetriNet::PetriNet(const std::set &files, std::shared_ptr stp) { const auto [net, m0, priorities] = readGrml(files); areAllTransitionsInStore(store, net); - std::tie(impl, register_functor) = - create(net, m0, final_marking, store, priorities, case_id, stp); + impl = std::make_shared(net, store, priorities, m0, final_marking, + case_id, stp); } PetriNet::PetriNet(const Net &net, const Marking &m0, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, - std::shared_ptr stp) { + std::shared_ptr stp) + : impl(std::make_shared(net, store, priorities, m0, final_marking, + case_id, stp)) { areAllTransitionsInStore(store, net); - std::tie(impl, register_functor) = - create(net, m0, final_marking, store, priorities, case_id, stp); } std::function PetriNet::registerTransitionCallback( const Transition &transition) const noexcept { - return [transition, this] { register_functor(transition); }; + return [transition, this] { + impl->active_reducers->enqueue([=](Petri &m) { + const auto t_index = toIndex(m.net.transition, transition); + m.active_transitions_n.push_back(t_index); + m.active_reducers->enqueue( + fireTransition(t_index, m.net.transition[t_index], + m.net.store[t_index], m.case_id, m.active_reducers)); + }); + }; } bool PetriNet::reuseApplication(const std::string &new_case_id) { From f7492830bc4ee1294a9157fc79e163492ef5bc8a Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 20:39:42 +0200 Subject: [PATCH 23/27] double checks --- examples/flight/flight.cc | 9 ++++---- symmetri/include/symmetri/symmetri.h | 26 +++++++++++------------ symmetri/symmetri.cc | 31 ++++++++++++++-------------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index 8b9b52d3..97ee9a4d 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -86,10 +86,11 @@ int main(int, char *argv[]) { auto gantt = std::thread([&] { while (running) { std::this_thread::sleep_for(std::chrono::seconds(3)); + if (!running) { + break; + } writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(getLog(bignet))); } - writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(getLog(bignet))); - std::cout << "gannt blocked" << std::endl; }); // Parallel to the PetriNet execution, we run a thread through which we @@ -117,7 +118,6 @@ int main(int, char *argv[]) { } std::cin.get(); }; - std::cout << "cin blocked" << std::endl; }); // this is where we call the blocking fire-function that executes the petri @@ -127,7 +127,8 @@ int main(int, char *argv[]) { // print the results and eventlog spdlog::info("Result of this net: {0}", printState(result)); printLog(el); - t.join(); // clean up gantt.join(); // clean up + t.join(); // clean up + writeMermaidHtmlToFile(symmetri::mermaidFromEventlog(el)); return 0; } diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index b7215eda..b5ff106f 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -7,6 +7,18 @@ #include "symmetri/tasks.h" #include "symmetri/types.h" +/** + * @brief The PetriNet class is a class that can create, configure and + * run a Petri net. + * + */ +class PetriNet; +symmetri::Result fire(const PetriNet &); +symmetri::Result cancel(const PetriNet &); +void pause(const PetriNet &); +void resume(const PetriNet &); +symmetri::Eventlog getLog(const PetriNet &); + namespace symmetri { /** * @brief A Store is a mapping from Transitions, represented by a string that is @@ -26,18 +38,6 @@ struct Petri; } // namespace symmetri -/** - * @brief The PetriNet class is a class that can create, configure and - * run a Petri net. - * - */ -class PetriNet; -symmetri::Result fire(const PetriNet &); -symmetri::Result cancel(const PetriNet &); -void pause(const PetriNet &); -void resume(const PetriNet &); -symmetri::Eventlog getLog(const PetriNet &); - class PetriNet final { public: /** @@ -114,7 +114,7 @@ class PetriNet final { friend symmetri::Eventlog(::getLog)(const PetriNet &); private: - std::shared_ptr + const std::shared_ptr impl; ///< Pointer to the implementation, all ///< information is stored in Petri }; diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 45389d2d..394c3544 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -43,22 +43,23 @@ using namespace symmetri; PetriNet::PetriNet(const std::set &files, const Marking &final_marking, const Store &store, const PriorityTable &priorities, const std::string &case_id, - std::shared_ptr stp) { - const auto [net, m0] = readPnml(files); - areAllTransitionsInStore(store, net); - impl = std::make_shared(net, store, priorities, m0, final_marking, - case_id, stp); -} + std::shared_ptr stp) + : impl([&] { + const auto [net, m0] = readPnml(files); + areAllTransitionsInStore(store, net); + return std::make_shared(net, store, priorities, m0, + final_marking, case_id, stp); + }()) {} PetriNet::PetriNet(const std::set &files, const Marking &final_marking, const Store &store, - const std::string &case_id, - std::shared_ptr stp) { - const auto [net, m0, priorities] = readGrml(files); - areAllTransitionsInStore(store, net); - impl = std::make_shared(net, store, priorities, m0, final_marking, - case_id, stp); -} + const std::string &case_id, std::shared_ptr stp) + : impl([&] { + const auto [net, m0, priorities] = readGrml(files); + areAllTransitionsInStore(store, net); + return std::make_shared(net, store, priorities, m0, + final_marking, case_id, stp); + }()) {} PetriNet::PetriNet(const Net &net, const Marking &m0, const Marking &final_marking, const Store &store, @@ -148,6 +149,7 @@ symmetri::Result cancel(const PetriNet &app) { model.net.transition[transition_index], state, symmetri::Clock::now()}); } + model.active_transitions_n.clear(); }); return {getLog(app), symmetri::State::UserExit}; @@ -172,8 +174,7 @@ void resume(const PetriNet &app) { }; symmetri::Eventlog getLog(const PetriNet &app) { - if (app.impl->thread_id_.load() && - app.impl->thread_id_.load().value() != getThreadId()) { + if (app.impl->thread_id_.load()) { std::promise el; std::future el_getter = el.get_future(); app.impl->active_reducers->enqueue([&](Petri &model) { From 4056108bf58675ee9433aec200d24f289ca77a8e Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Thu, 3 Aug 2023 21:07:12 +0200 Subject: [PATCH 24/27] cleaning cmake --- CMakeLists.txt | 14 ++++++++++---- symmetri/eventlog_parsers.cc | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd43acf2..ae480a27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,29 @@ cmake_minimum_required(VERSION 3.10) + # Read version from the package.xml file. file(READ ${CMAKE_CURRENT_SOURCE_DIR}/package.xml package_xml_str) + if(NOT package_xml_str MATCHES "([0-9]+.[0-9]+.[0-9]+)") message(FATAL_ERROR "Could not parse project version from package.xml. Aborting.") endif() + project(symmetri VERSION ${CMAKE_MATCH_1}) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized -Wno-psabi -Wno-unused-parameter -Werror -Wall -Wextra -O3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -pedantic") if(BUILD_TESTING) enable_testing() + if(ASAN_BUILD AND NOT TSAN_BUILD) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address,undefined -fno-omit-frame-pointer -O0") elseif(TSAN_BUILD AND NOT ASAN_BUILD) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=thread -fno-omit-frame-pointer -O0") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -O3") endif() endif() @@ -26,7 +32,7 @@ add_subdirectory(symmetri) # some examples using the lib if(BUILD_EXAMPLES) - add_subdirectory(examples/flight) - add_subdirectory(examples/hello_world) - add_subdirectory(examples/model_benchmark) + add_subdirectory(examples/flight) + add_subdirectory(examples/hello_world) + add_subdirectory(examples/model_benchmark) endif() diff --git a/symmetri/eventlog_parsers.cc b/symmetri/eventlog_parsers.cc index f37bc933..febacd1d 100644 --- a/symmetri/eventlog_parsers.cc +++ b/symmetri/eventlog_parsers.cc @@ -31,7 +31,7 @@ std::string mermaidFromEventlog(symmetri::Eventlog el) { std::stringstream mermaid; mermaid << "\n---\ndisplayMode : compact\n---\ngantt\ntitle A Gantt " - "Diagram\ndateFormat x\naxisFormat \%H:\%M:\%S\n"; + "Diagram\ndateFormat x\naxisFormat %H:%M:%S\n"; std::string current_section(""); for (auto it = el.begin(); std::next(it) != el.end(); it = std::next(it)) { const auto &start = *it; From b9e7844bd835bfc9137959d5d24bd379fd688c0d Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Fri, 4 Aug 2023 09:00:17 +0200 Subject: [PATCH 25/27] only trigger if net is still active --- CMakeLists.txt | 1 - symmetri/model_utilities.cc | 2 +- symmetri/symmetri.cc | 27 +++++++++++++++------------ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae480a27..fad84c56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -pedantic") if(BUILD_TESTING) enable_testing() - if(ASAN_BUILD AND NOT TSAN_BUILD) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address,undefined -fno-omit-frame-pointer -O0") elseif(TSAN_BUILD AND NOT ASAN_BUILD) diff --git a/symmetri/model_utilities.cc b/symmetri/model_utilities.cc index ba60ca01..c11b15c5 100644 --- a/symmetri/model_utilities.cc +++ b/symmetri/model_utilities.cc @@ -12,7 +12,7 @@ bool canFire(const SmallVector &pre, const std::vector &tokens) { return std::count(tokens.begin(), tokens.end(), m_p) >= std::count(pre.begin(), pre.end(), m_p); }); -}; +} gch::small_vector possibleTransitions( const std::vector &tokens, diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 394c3544..9083bc4a 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -73,13 +73,17 @@ PetriNet::PetriNet(const Net &net, const Marking &m0, std::function PetriNet::registerTransitionCallback( const Transition &transition) const noexcept { return [transition, this] { - impl->active_reducers->enqueue([=](Petri &m) { - const auto t_index = toIndex(m.net.transition, transition); - m.active_transitions_n.push_back(t_index); - m.active_reducers->enqueue( - fireTransition(t_index, m.net.transition[t_index], - m.net.store[t_index], m.case_id, m.active_reducers)); - }); + if (impl->thread_id_.load()) { + impl->active_reducers->enqueue([=](Petri &m) { + if (m.thread_id_.load()) { + const auto t_index = toIndex(m.net.transition, transition); + m.active_transitions_n.push_back(t_index); + m.active_reducers->enqueue(fireTransition( + t_index, m.net.transition[t_index], m.net.store[t_index], + m.case_id, m.active_reducers)); + } + }); + } }; } @@ -124,16 +128,15 @@ symmetri::Result fire(const PetriNet &app) { m.state = m.active_transitions_n.size() == 0 ? State::Deadlock : m.state; } } - + m.thread_id_.store(std::nullopt); // empty reducers m.active_transitions_n.clear(); while (m.active_reducers->try_dequeue(f)) { f(m); } - m.thread_id_.store(std::nullopt); return {m.event_log, m.state}; -}; +} symmetri::Result cancel(const PetriNet &app) { app.impl->active_reducers->enqueue([=](symmetri::Petri &model) { @@ -162,7 +165,7 @@ void pause(const PetriNet &app) { pause(model.net.store.at(transition_index)); } }); -}; +} void resume(const PetriNet &app) { app.impl->active_reducers->enqueue([](symmetri::Petri &model) { @@ -171,7 +174,7 @@ void resume(const PetriNet &app) { resume(model.net.store.at(transition_index)); } }); -}; +} symmetri::Eventlog getLog(const PetriNet &app) { if (app.impl->thread_id_.load()) { From 669e19e8e0730fdd54c42eebfe43e41b7be84905 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Fri, 4 Aug 2023 10:07:28 +0200 Subject: [PATCH 26/27] getMarking --- symmetri/include/symmetri/symmetri.h | 2 ++ symmetri/model.cc | 12 ++++++++++++ symmetri/model.h | 8 ++++++++ symmetri/symmetri.cc | 27 +++++++++++++++------------ 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/symmetri/include/symmetri/symmetri.h b/symmetri/include/symmetri/symmetri.h index b5ff106f..62924833 100644 --- a/symmetri/include/symmetri/symmetri.h +++ b/symmetri/include/symmetri/symmetri.h @@ -99,6 +99,8 @@ class PetriNet final { std::function registerTransitionCallback( const std::string &transition) const noexcept; + std::vector getMarking() const noexcept; + /** * @brief reuseApplication resets the application such that the same net can * be used again after an cancel call. You do need to supply a new case_id diff --git a/symmetri/model.cc b/symmetri/model.cc index 5443b132..f204c727 100644 --- a/symmetri/model.cc +++ b/symmetri/model.cc @@ -218,6 +218,18 @@ std::vector Petri::getMarking() const { return marking; } +Eventlog Petri::getLog() const { + Eventlog eventlog = event_log; + // get event log from parent nets: + for (size_t pt_idx : active_transitions_n) { + auto sub_el = ::getLog(net.store[pt_idx]); + if (!sub_el.empty()) { + eventlog.insert(eventlog.end(), sub_el.begin(), sub_el.end()); + } + } + return eventlog; +} + std::vector Petri::getActiveTransitions() const { std::vector transition_list; if (active_transitions_n.size() > 0) { diff --git a/symmetri/model.h b/symmetri/model.h index a57ef0a9..1ccd3e9d 100644 --- a/symmetri/model.h +++ b/symmetri/model.h @@ -119,6 +119,14 @@ struct Petri { */ std::vector getMarking() const; + /** + * @brief get the current eventlog, also copies in all child eventlogs of + * active petri nets. + * + * @return Eventlog + */ + Eventlog getLog() const; + /** * @brief Get a vector of transitions that are *active*. Active means that * they are either in the transition queue or its transition has been fired. diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 9083bc4a..802a7eb5 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -87,6 +87,18 @@ std::function PetriNet::registerTransitionCallback( }; } +std::vector PetriNet::getMarking() const noexcept { + if (impl->thread_id_.load()) { + std::promise> el; + std::future> el_getter = el.get_future(); + impl->active_reducers->enqueue( + [&](Petri &model) { el.set_value(model.getMarking()); }); + return el_getter.get(); + } else { + return impl->getMarking(); + } +} + bool PetriNet::reuseApplication(const std::string &new_case_id) { if (!impl->thread_id_.load().has_value() && new_case_id != impl->case_id) { impl->case_id = new_case_id; @@ -180,19 +192,10 @@ symmetri::Eventlog getLog(const PetriNet &app) { if (app.impl->thread_id_.load()) { std::promise el; std::future el_getter = el.get_future(); - app.impl->active_reducers->enqueue([&](Petri &model) { - auto eventlog = model.event_log; - // get event log from parent nets: - for (size_t pt_idx : model.active_transitions_n) { - auto sub_el = getLog(model.net.store[pt_idx]); - if (!sub_el.empty()) { - eventlog.insert(eventlog.end(), sub_el.begin(), sub_el.end()); - } - } - el.set_value(std::move(eventlog)); - }); + app.impl->active_reducers->enqueue( + [&](Petri &model) { el.set_value(model.getLog()); }); return el_getter.get(); } else { - return app.impl->event_log; + return app.impl->getLog(); } } From cd47155f209852ba1668c506e4eb75b8bc1569b3 Mon Sep 17 00:00:00 2001 From: Thomas Horstink Date: Sat, 5 Aug 2023 10:02:01 +0200 Subject: [PATCH 27/27] cleaning --- .gitignore | 1 + examples/flight/flight.cc | 1 + examples/hello_world/main.cc | 1 + examples/model_benchmark/model_benchmark.cc | 1 + symmetri/include/symmetri/types.h | 68 --------------- symmetri/include/symmetri/utilities.hpp | 95 +++++++++++++++++++++ symmetri/symmetri.cc | 1 + symmetri/tests/test_bugs.cc | 1 + symmetri/tests/test_model.cc | 2 + symmetri/tests/test_parser.cc | 2 + symmetri/tests/test_priorities.cc | 2 + symmetri/tests/test_types.cc | 1 + symmetri/types.cpp | 53 ------------ 13 files changed, 108 insertions(+), 121 deletions(-) create mode 100644 symmetri/include/symmetri/utilities.hpp diff --git a/.gitignore b/.gitignore index 432de573..94526814 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build install docs/html .vscode +examples/flight/mermaid.html # Prerequisites *.d diff --git a/examples/flight/flight.cc b/examples/flight/flight.cc index 97ee9a4d..be88f3d4 100644 --- a/examples/flight/flight.cc +++ b/examples/flight/flight.cc @@ -5,6 +5,7 @@ #include "symmetri/parsers.h" #include "symmetri/symmetri.h" +#include "symmetri/utilities.hpp" #include "transition.hpp" using namespace symmetri; diff --git a/examples/hello_world/main.cc b/examples/hello_world/main.cc index 1d78db31..f5988965 100644 --- a/examples/hello_world/main.cc +++ b/examples/hello_world/main.cc @@ -9,6 +9,7 @@ #include #include "symmetri/symmetri.h" +#include "symmetri/utilities.hpp" // Before main, we define a bunch of functions that can be bound to // transitions in the petri net. diff --git a/examples/model_benchmark/model_benchmark.cc b/examples/model_benchmark/model_benchmark.cc index 56a24949..e166b237 100644 --- a/examples/model_benchmark/model_benchmark.cc +++ b/examples/model_benchmark/model_benchmark.cc @@ -2,6 +2,7 @@ #include "symmetri/parsers.h" #include "symmetri/symmetri.h" +#include "symmetri/utilities.hpp" int main(int, char *argv[]) { spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%f] [%^%l%$] [thread %t] %v"); diff --git a/symmetri/include/symmetri/types.h b/symmetri/include/symmetri/types.h index 644c1c43..1fbfa548 100644 --- a/symmetri/include/symmetri/types.h +++ b/symmetri/include/symmetri/types.h @@ -60,72 +60,4 @@ using PriorityTable = std::vector>; ///< Priority is limited from ///< -128 to 127 -/** - * @brief Checks if the markings are exactly the same. Note that this uses a - * different type for Marking compared to the Marking type used to - * construct a net (an unordered map of strings). In this format the amount of - * tokens in a particular place is represented by how often that place occurs in - * the vector. For example: {"A","A","B"} is a marking with two tokens in place - * "A" and one token in place "B". This format does not have the overhead of - * mentioning all empty places. - * - * @tparam T - * @param m1 - * @param m2 - * @return true - * @return false - */ -template -bool MarkingEquality(const std::vector& m1, const std::vector& m2); - -/** - * @brief Checks if marking is at least a subset of final_marking. Note - * that this uses a different type for Marking compared to the Marking - * type used to construct a net (an unordered map of strings). In this - * format the amount of tokens in a particular place is represented by how often - * that place occurs in the vector. For example: {"A","A","B"} is a marking with - * two tokens in place "A" and one token in place "B". This format does not have - * the overhead of mentioning all empty places. - * - * @tparam T - * @param marking - * @param final_marking - * @return true - * @return false - */ -template -bool MarkingReached(const std::vector& marking, - const std::vector& final_marking); - -/** - * @brief Checks if two petri-nets have equal amount of arcs between places - * and transitions of the same name. - * - * @param net1 - * @param net2 - * @return true - * @return false - */ -bool stateNetEquality(const Net& net1, const Net& net2); - -/** - * @brief Calculates a hash given an event log. This hash is only influenced by - * the order of the completions of transitions and if the output of those - * transitions is Completed, or something else. - * - * @param event_log An eventlog, can both be from a terminated or a still active - * net. - * @return size_t The hashed result. - */ -size_t calculateTrace(const Eventlog& event_log) noexcept; - -/** - * @brief A convenience function to get a string representation of the - * state-enum. - * - * @param s The State - * @return std::string The State as a human readable string. - */ -std::string printState(State s) noexcept; - } // namespace symmetri diff --git a/symmetri/include/symmetri/utilities.hpp b/symmetri/include/symmetri/utilities.hpp new file mode 100644 index 00000000..895abf5d --- /dev/null +++ b/symmetri/include/symmetri/utilities.hpp @@ -0,0 +1,95 @@ +#pragma once +#include + +#include "symmetri/types.h" + +namespace symmetri { + +/** + * @brief Checks if the markings are exactly the same. Note that this uses a + * different type for Marking compared to the Marking type used to + * construct a net (an unordered map of strings). In this format the amount of + * tokens in a particular place is represented by how often that place occurs in + * the vector. For example: {"A","A","B"} is a marking with two tokens in place + * "A" and one token in place "B". This format does not have the overhead of + * mentioning all empty places. + * + * @tparam T + * @param m1 + * @param m2 + * @return true + * @return false + */ +template +bool MarkingEquality(const std::vector& m1, const std::vector& m2) { + auto m1_sorted = m1; + auto m2_sorted = m2; + std::sort(m1_sorted.begin(), m1_sorted.end()); + std::sort(m2_sorted.begin(), m2_sorted.end()); + return m1_sorted == m2_sorted; +} + +/** + * @brief Checks if marking is at least a subset of final_marking. Note + * that this uses a different type for Marking compared to the Marking + * type used to construct a net (an unordered map of strings). In this + * format the amount of tokens in a particular place is represented by how often + * that place occurs in the vector. For example: {"A","A","B"} is a marking with + * two tokens in place "A" and one token in place "B". This format does not have + * the overhead of mentioning all empty places. + * + * @tparam T + * @param marking + * @param final_marking + * @return true + * @return false + */ +template +bool MarkingReached(const std::vector& marking, + const std::vector& final_marking) { + if (final_marking.empty()) { + return false; + } + auto unique = final_marking; + std::sort(unique.begin(), unique.end()); + auto last = std::unique(unique.begin(), unique.end()); + unique.erase(last, unique.end()); + + return std::all_of(std::begin(unique), std::end(unique), [&](const auto& p) { + return std::count(marking.begin(), marking.end(), p) == + std::count(final_marking.begin(), final_marking.end(), p); + }); +} + +/** + * @brief Checks if two petri-nets have equal amount of arcs between places + * and transitions of the same name. + * + * @param net1 + * @param net2 + * @return true + * @return false + */ +bool stateNetEquality(const Net& net1, const Net& net2); + +/** + * @brief Calculates a hash given an event log. This hash is only influenced by + * the order of the completions of transitions and if the output of those + * transitions is Completed, or something else. + * + * @param event_log An eventlog, can both be from a terminated or a still active + * net. + * @return size_t The hashed result. + */ +size_t calculateTrace(const Eventlog& event_log) noexcept; + +/** + * @brief A convenience function to get a string representation of the + * state-enum. + * + * @param s The State + * @return std::string The State as a human readable string. + */ +std::string printState(State s) noexcept; + +} // namespace symmetri diff --git a/symmetri/symmetri.cc b/symmetri/symmetri.cc index 802a7eb5..f8db5888 100644 --- a/symmetri/symmetri.cc +++ b/symmetri/symmetri.cc @@ -8,6 +8,7 @@ #include "model.h" #include "symmetri/parsers.h" +#include "symmetri/utilities.hpp" namespace symmetri { diff --git a/symmetri/tests/test_bugs.cc b/symmetri/tests/test_bugs.cc index 10942ec5..1b50958a 100644 --- a/symmetri/tests/test_bugs.cc +++ b/symmetri/tests/test_bugs.cc @@ -2,6 +2,7 @@ #include #include "model.h" +#include "symmetri/utilities.hpp" using namespace symmetri; diff --git a/symmetri/tests/test_model.cc b/symmetri/tests/test_model.cc index 87602b0b..e1de9f33 100644 --- a/symmetri/tests/test_model.cc +++ b/symmetri/tests/test_model.cc @@ -2,6 +2,8 @@ #include #include "model.h" +#include "symmetri/utilities.hpp" + using namespace symmetri; using namespace moodycamel; diff --git a/symmetri/tests/test_parser.cc b/symmetri/tests/test_parser.cc index ffe789e9..9b2590b3 100644 --- a/symmetri/tests/test_parser.cc +++ b/symmetri/tests/test_parser.cc @@ -3,6 +3,8 @@ #include #include "symmetri/parsers.h" +#include "symmetri/utilities.hpp" + using namespace symmetri; TEST_CASE("Load p1.pnml net") { diff --git a/symmetri/tests/test_priorities.cc b/symmetri/tests/test_priorities.cc index a9cfb7d6..5b9709bd 100644 --- a/symmetri/tests/test_priorities.cc +++ b/symmetri/tests/test_priorities.cc @@ -3,6 +3,8 @@ #include #include "model.h" +#include "symmetri/utilities.hpp" + using namespace symmetri; using namespace moodycamel; diff --git a/symmetri/tests/test_types.cc b/symmetri/tests/test_types.cc index 8cf34c7b..a5c7c251 100644 --- a/symmetri/tests/test_types.cc +++ b/symmetri/tests/test_types.cc @@ -1,6 +1,7 @@ #include #include "symmetri/types.h" +#include "symmetri/utilities.hpp" using namespace symmetri; diff --git a/symmetri/types.cpp b/symmetri/types.cpp index 01cc1f72..9ac7dad7 100644 --- a/symmetri/types.cpp +++ b/symmetri/types.cpp @@ -4,59 +4,6 @@ #include namespace symmetri { -template <> -bool MarkingEquality(const std::vector& m1, - const std::vector& m2) { - auto m1_sorted = m1; - auto m2_sorted = m2; - std::sort(m1_sorted.begin(), m1_sorted.end()); - std::sort(m2_sorted.begin(), m2_sorted.end()); - return m1_sorted == m2_sorted; -} -template <> -bool MarkingReached(const std::vector& marking, - const std::vector& final_marking) { - if (final_marking.empty()) { - return false; - } - auto unique = final_marking; - std::sort(unique.begin(), unique.end()); - auto last = std::unique(unique.begin(), unique.end()); - unique.erase(last, unique.end()); - - return std::all_of(std::begin(unique), std::end(unique), [&](const auto& p) { - return std::count(marking.begin(), marking.end(), p) == - std::count(final_marking.begin(), final_marking.end(), p); - }); -} - -template <> -bool MarkingEquality(const std::vector& m1, - const std::vector& m2) { - auto m1_sorted = m1; - auto m2_sorted = m2; - std::sort(m1_sorted.begin(), m1_sorted.end()); - std::sort(m2_sorted.begin(), m2_sorted.end()); - return m1_sorted == m2_sorted; -} -template <> -bool MarkingReached( - const std::vector& marking, - const std::vector& final_marking) { - if (final_marking.empty()) { - return false; - } - auto unique = final_marking; - std::sort(unique.begin(), unique.end()); - auto last = std::unique(unique.begin(), unique.end()); - unique.erase(last, unique.end()); - - return std::all_of(std::begin(unique), std::end(unique), [&](const auto& p) { - return std::count(marking.begin(), marking.end(), p) == - std::count(final_marking.begin(), final_marking.end(), p); - }); -} - bool stateNetEquality(const Net& net1, const Net& net2) { if (net1.size() != net2.size()) { return false;