Skip to content

Mutation Clean Slate #77

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: mutations-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ cmake-build-debug
cmake-build-release
.idea
result
Makefile
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ endif()
project(${LIB_TARGET} LANGUAGES CXX VERSION 0.0.1)

# require C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ add_subdirectory(count)
add_subdirectory(ports)
add_subdirectory(hello)
add_subdirectory(power_train)
add_subdirectory(multiport_mutation)
2 changes: 1 addition & 1 deletion examples/count/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using namespace reactor;
using namespace std::chrono_literals;

class Count : public Reactor {
class Count final : public Reactor {
private:
// actions
Timer timer{"timer", this};
Expand Down
2 changes: 1 addition & 1 deletion examples/hello/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using namespace reactor;
using namespace std::chrono_literals;

class Hello : public Reactor {
class Hello final : public Reactor {
private:
// actions
Timer timer{"timer", this, 1s, 2s};
Expand Down
3 changes: 3 additions & 0 deletions examples/multiport_mutation/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
add_executable(mutation_multiports main.cc)
target_link_libraries(mutation_multiports reactor-cpp)
add_dependencies(examples mutation_multiports)
48 changes: 48 additions & 0 deletions examples/multiport_mutation/consumer.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2025 TU Dresden
* All rights reserved.
*
* Authors:
* Tassilo Tanneberger
*/

#ifndef MULTIPORT_MUTATION_CONSUMER_HH
#define MULTIPORT_MUTATION_CONSUMER_HH

#include <reactor-cpp/reactor-cpp.hh>
#include <reactor-cpp/scopes.hh>

using namespace reactor;
using namespace std::chrono_literals;

class Consumer final : public Reactor { // NOLINT
class Inner : public Scope {
Inner(Reaction* reaction, std::size_t index)
: Scope(reaction)
, index_(index) {}
std::size_t index_ = 0;

void reaction_1(const Input<unsigned>& in) const {
// std::cout << "consumer: " << index_ << " received value:" << *in.get() << '\n';
}

friend Consumer;
};

Inner _lf_inner;
Reaction handle{"handle", 1, false, this, [this]() { _lf_inner.reaction_1(this->in); }};

public:
Consumer(const std::string& name, Environment* env, std::size_t index)
: Reactor(name, env)
, _lf_inner(&handle, index) {
std::cout << "creating instance of consumer" << '\n';
}
~Consumer() override { std::cout << "Consumer Object is deleted" << '\n'; };

Input<unsigned> in{"in", this}; // NOLINT

void assemble() override { handle.declare_trigger(&in); }
};

#endif // MULTIPORT_MUTATION_CONSUMER_HH
69 changes: 69 additions & 0 deletions examples/multiport_mutation/load_balancer.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2025 TU Dresden
* All rights reserved.
*
* Authors:
* Tassilo Tanneberger
*/

#ifndef MULTIPORT_MUTATION_LOAD_BALANCER_HH
#define MULTIPORT_MUTATION_LOAD_BALANCER_HH

#include <reactor-cpp/mutations/multiport.hh>
#include <reactor-cpp/reactor-cpp.hh>

using namespace reactor;
using namespace std::chrono_literals;

class LoadBalancer final : public Reactor { // NOLINT
class Inner : public MutableScope {
explicit Inner(Reaction* reaction)
: MutableScope(reaction) {}

// reaction bodies
static void reaction_1(const Input<unsigned>& inbound, Output<unsigned>& scale_bank,
Multiport<Output<unsigned>>& outbound) {
if (std::rand() % 15 == 0) { // NOLINT

std::cout << "triggering mutation" << std::endl;
scale_bank.set(std::rand() % 20 + 1); // NOLINT
}
const unsigned outbound_port = std::rand() % outbound.size(); // NOLINT

std::cout << "forwarding to: " << outbound_port << std::endl;
outbound[outbound_port].set(inbound.get());
}

friend LoadBalancer;
};

Inner _lf_inner;
Reaction process{"process", 1, false, this,
[this]() { Inner::reaction_1(this->inbound, this->scale_bank, this->out); }};

public:
LoadBalancer(const std::string& name, Environment* env)
: Reactor(name, env)
, _lf_inner(&process) {
out.reserve(4);
for (size_t _lf_idx = 0; _lf_idx < 4; _lf_idx++) {
out.create_new_port();
}
}
~LoadBalancer() override = default;

ModifableMultiport<Output<unsigned>> out{"out", this}; // NOLINT
std::size_t out_size_ = 0;

Input<unsigned> inbound{"inbound", this}; // NOLINT
Output<unsigned> scale_bank{"scale_bank", this}; // NOLINT

void assemble() override {
for (auto& _lf_port : out) {
process.declare_antidependency(&_lf_port);
}
process.declare_trigger(&inbound);
}
};

#endif // MULTIPORT_MUTATION_LOAD_BALANCER_HH
96 changes: 96 additions & 0 deletions examples/multiport_mutation/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2025 TU Dresden
* All rights reserved.
*
* Authors:
* Tassilo Tanneberger
*/

#include <iostream>
#include <memory>

#include <reactor-cpp/reactor-cpp.hh>

#include "./consumer.hh"
#include "./load_balancer.hh"
#include "./multiport_to_bank.hh"
#include "./producer.hh"

class Deployment final : public Reactor { // NOLINT

std::unique_ptr<Producer> producer_;
std::unique_ptr<LoadBalancer> load_balancer_;
std::vector<std::unique_ptr<Consumer>> consumers_;

Reaction scale_bank{"scale_bank", 1, true, this, [this]() {
this->_inner.reaction_1(this->scale, this->consumers_, load_balancer_->out);
//
}};

class InnerNonMutable : public Scope {};
class Inner : public MutableScope {
int state = 0;

public:
explicit Inner(Reaction* reaction)
: MutableScope(reaction) {}
void reaction_1(const Input<unsigned>& scale, std::vector<std::unique_ptr<Consumer>>& reactor_bank,
ModifableMultiport<Output<unsigned>>& load_balancer) {
std::cout << "mutation reaction is being executed" << std::endl;
std::size_t new_size = *scale.get();

std::function lambda = [](Environment* env, std::size_t index) {
std::string _lf_inst_name = "consumer_" + std::to_string(index);
return std::make_unique<Consumer>(_lf_inst_name, env, index);
};

std::function get_input_port = [](const std::unique_ptr<Consumer>& consumer) { return &consumer->in; };

const auto rescale = std::make_shared<ResizeMultiportToBank<unsigned, Consumer>>(
reaction_, &load_balancer, &reactor_bank, get_input_port, lambda, new_size);

add_to_transaction(rescale);
commit_transaction(true);
}

friend LoadBalancer;
};

Inner _inner;

public:
Deployment(const std::string& name, Environment* env)
: Reactor(name, env)
, _inner(&scale_bank)
, producer_(std::make_unique<Producer>("producer", environment()))
, load_balancer_(std::make_unique<LoadBalancer>("load_balancer", environment())) {
std::cout << "creating instance of deployment" << '\n';
consumers_.reserve(4);
for (size_t _lf_idx = 0; _lf_idx < 4; _lf_idx++) {
std::string _lf_inst_name = "consumer_" + std::to_string(_lf_idx);
consumers_.push_back(std::make_unique<Consumer>(_lf_inst_name, environment(), _lf_idx));
}
}
~Deployment() override = default;

Input<unsigned> scale{"scale", this}; // NOLINT

void assemble() override {
for (size_t _lf_idx = 0; _lf_idx < 4; _lf_idx++) {
environment()->draw_connection(load_balancer_->out[_lf_idx], consumers_[_lf_idx]->in, ConnectionProperties{});
environment()->draw_connection(producer_->value, load_balancer_->inbound, ConnectionProperties{});
}
environment()->draw_connection(load_balancer_->scale_bank, scale, ConnectionProperties{});
scale_bank.declare_trigger(&this->scale);
}
};

auto main() -> int {
Environment env{4, true};
auto deployment = std::make_unique<Deployment>("c1", &env);
env.optimize();
env.assemble();
auto thread = env.startup();
thread.join();
return 0;
}
98 changes: 98 additions & 0 deletions examples/multiport_mutation/multiport_to_bank.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2025 TU Dresden
* All rights reserved.
*
* Authors:
* Tassilo Tanneberger
*/

#ifndef MULTIPORT_MUTATION_MULTIPORT_TO_BANK_HH
#define MULTIPORT_MUTATION_MULTIPORT_TO_BANK_HH

#include <reactor-cpp/multiport.hh>
#include <reactor-cpp/mutations.hh>
#include <reactor-cpp/mutations/bank.hh>
#include <reactor-cpp/mutations/connection.hh>
#include <reactor-cpp/mutations/multiport.hh>
#include <reactor-cpp/port.hh>
#include <reactor-cpp/reactor.hh>

#include "../../lib/mutations/bank.cc"
#include "../../lib/mutations/connection.cc"
#include "../../lib/mutations/multiport.cc"

#include <functional>

namespace reactor {

template <class PortType, class ReactorType> class ResizeMultiportToBank : public Mutation {
ModifableMultiport<Output<PortType>>* multiport_;
std::vector<std::unique_ptr<ReactorType>>* bank_;
std::function<Input<PortType>*(const std::unique_ptr<ReactorType>&)> get_input_port_;
std::function<std::unique_ptr<ReactorType>(Environment* env, std::size_t index)> create_lambda_;
std::size_t new_size_ = 0;

public:
ResizeMultiportToBank(Reaction* reaction, ModifableMultiport<Output<PortType>>* multiport,
std::vector<std::unique_ptr<ReactorType>>* bank,
std::function<Input<PortType>*(const std::unique_ptr<ReactorType>&)> get_input_port,
std::function<std::unique_ptr<ReactorType>(Environment* env, std::size_t index)> create_lambda,
std::size_t new_size)
: Mutation(reaction)
, multiport_(multiport)
, bank_(bank)
, get_input_port_(get_input_port)
, create_lambda_(create_lambda)
, new_size_(new_size) {}

~ResizeMultiportToBank() = default;
auto run() -> MutationResult override {
if (multiport_->size() != bank_->size()) {
return NotMatchingBankSize;
}
auto old_size = multiport_->size();

if (new_size_ > old_size) {
auto change_multiport_size =
std::make_shared<MutationChangeOutputMultiportSize<unsigned>>(reaction_, multiport_, new_size_);

change_multiport_size->run();

auto change_bank_size = std::make_shared<MutationChangeBankSize<std::unique_ptr<ReactorType>>>(
reaction_, bank_, new_size_, create_lambda_);

change_bank_size->run();

for (auto i = old_size; i < new_size_; i++) {
auto add_conn = std::make_shared<MutationAddConnection<Output<PortType>, Input<PortType>>>(
reaction_, &(*multiport_)[i], get_input_port_((*bank_)[i]), true);

add_conn->run();
}
} else if (new_size_ < old_size) {
for (auto i = old_size - 1; i >= new_size_; i--) {
auto add_conn = std::make_shared<MutationAddConnection<Output<PortType>, Input<PortType>>>(
reaction_, &(*multiport_)[i], get_input_port_((*bank_)[i]), false);

add_conn->run();
}

auto change_multiport_size =
std::make_shared<MutationChangeOutputMultiportSize<unsigned>>(reaction_, multiport_, new_size_);

change_multiport_size->run();

auto change_bank_size = std::make_shared<MutationChangeBankSize<std::unique_ptr<ReactorType>>>(
reaction_, bank_, new_size_, create_lambda_);

change_bank_size->run();
}

return Success;
}

auto rollback() -> MutationResult { return Success; }
};
} // namespace reactor

#endif // MULTIPORT_MUTATION_MULTIPORT_TO_BANK_HH
Loading
Loading