Program Listing for File port.cc

Return to documentation for file (lib/port.cc)

/*
 * Copyright (C) 2019 TU Dresden
 * All rights reserved.
 *
 * Authors:
 *   Christian Menard
 */

#include "reactor-cpp/port.hh"

#include "reactor-cpp/assert.hh"
#include "reactor-cpp/environment.hh"
#include "reactor-cpp/reaction.hh"

namespace reactor {

void BasePort::base_bind_to(BasePort* port) {
  reactor_assert(port != nullptr);
  reactor_assert(this->environment() == port->environment()); // NOLINT
  validate(!port->has_inward_binding(), "Ports may only be connected once");
  validate(!port->has_anti_dependencies(), "Ports with anti dependencies may not be connected to other ports");
  assert_phase(this, Environment::Phase::Assembly);
  if (this->is_input() && port->is_input()) {
    validate(this->container() == port->container()->container(),
             "An input port A may only be bound to another input port B if B is contained by a reactor that in turn is "
             "contained by the reactor of A");
  } else if (this->is_input() && port->is_output()) {
    validate(
        this->container() == port->container(),
        "An input port A may only be bound directly to an output port B if A and B are contained by the same reactor.");
  } else if (this->is_output() && port->is_input()) {
    validate(this->container()->container() == port->container()->container(),
             "An output port can only be bound to an input port if both ports belong to reactors in the same "
             "hierarichal level");
  } else if (this->is_output() && port->is_output()) {
    validate(this->container()->container() == port->container(),
             "An output port A may only be bound to another output port B if A is contained by a reactor that in turn "
             "is contained by the reactor of B");
  } else {
    throw std::runtime_error("invalid connection");
  }

  port->inward_binding_ = this;
  reactor_assert(this->outward_bindings_.insert(port).second);
}

void BasePort::register_dependency(Reaction* reaction, bool is_trigger) noexcept {
  reactor_assert(reaction != nullptr);
  reactor_assert(this->environment() == reaction->environment()); // NOLINT
  validate(!this->has_outward_bindings(), "Dependencies may no be declared on ports with an outward binding!");
  assert_phase(this, Environment::Phase::Assembly);

  if (this->is_input()) {
    validate(this->container() == reaction->container(), "Dependent input ports must belong to the same reactor as the "
                                                         "reaction");
  } else {
    validate(this->container()->container() == reaction->container(),
             "Dependent output ports must belong to a contained reactor");
  }

  reactor_assert(dependencies_.insert(reaction).second);
  if (is_trigger) {
    reactor_assert(triggers_.insert(reaction).second);
  }
}

void BasePort::register_antidependency(Reaction* reaction) noexcept {
  reactor_assert(reaction != nullptr);
  reactor_assert(this->environment() == reaction->environment()); // NOLINT
  validate(!this->has_inward_binding(), "Antidependencies may no be declared on ports with an inward binding!");
  assert_phase(this, Environment::Phase::Assembly);

  if (this->is_output()) {
    validate(this->container() == reaction->container(),
             "Antidependent output ports must belong to the same reactor as "
             "the reaction");
  } else {
    validate(this->container()->container() == reaction->container(),
             "Antidependent input ports must belong to a contained reactor");
  }

  reactor_assert(anti_dependencies_.insert(reaction).second);
}

[[maybe_unused]] auto Port<void>::typed_outward_bindings() const noexcept -> const std::set<Port<void>*>& {
  // this is undefined behavior and should be changed
  return reinterpret_cast<const std::set<Port<void>*>&>(outward_bindings()); // NOLINT C++20 std::bit_cast
}

auto Port<void>::typed_inward_binding() const noexcept -> Port<void>* {
  // we can use a reinterpret cast here since we know that this port is always
  // connected with another Port<T>.
  return reinterpret_cast<Port<void>*>(inward_binding()); // NOLINT
}

void Port<void>::set() {
  validate(!has_inward_binding(), "set() may only be called on a ports that do not have an inward "
                                  "binding!");
  auto* scheduler = environment()->scheduler();
  scheduler->set_port(this);
  this->present_ = true;
}

} // namespace reactor