diff --git a/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h b/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h index f814d5b..d7993b2 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h +++ b/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h @@ -35,6 +35,7 @@ class ClockListener { public: virtual ~ClockListener() {} virtual void OnClock(int /*clock*/) = 0; + virtual bool OnHasCycle(ClockListener * /*node*/) { return false; } }; } // namespace internal diff --git a/dataflow_cpp/include/embb/dataflow/internal/in.h b/dataflow_cpp/include/embb/dataflow/internal/in.h index 8e39a79..15ef5db 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/in.h +++ b/dataflow_cpp/include/embb/dataflow/internal/in.h @@ -73,6 +73,10 @@ class In { bool IsConnected() const { return connected_; } void SetConnected() { connected_ = true; } + bool HasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } + void SetSlices(int slices) { if (0 < slices_) { for (int ii = 0; ii < slices_; ii++) { diff --git a/dataflow_cpp/include/embb/dataflow/internal/inputs.h b/dataflow_cpp/include/embb/dataflow/internal/inputs.h index 207e2a3..102c848 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/inputs.h +++ b/dataflow_cpp/include/embb/dataflow/internal/inputs.h @@ -117,6 +117,9 @@ class InputsOnClock(clock); } } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } bool IsFullyConnected() { return this->template Get<0>().IsConnected(); } @@ -185,6 +188,9 @@ class InputsOnClock(clock); } } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } bool IsFullyConnected() { return this->template Get<0>().IsConnected() & this->template Get<1>().IsConnected(); @@ -259,6 +265,9 @@ class InputsOnClock(clock); } } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } bool IsFullyConnected() { return this->template Get<0>().IsConnected() & this->template Get<1>().IsConnected() & @@ -338,6 +347,9 @@ class Inputs listener_->OnClock(clock); } } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } bool IsFullyConnected() { return this->template Get<0>().IsConnected() & this->template Get<1>().IsConnected() & @@ -424,6 +436,9 @@ class Inputs listener_->OnClock(clock); } } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } bool IsFullyConnected() { return this->template Get<0>().IsConnected() && this->template Get<1>().IsConnected() & diff --git a/dataflow_cpp/include/embb/dataflow/internal/node.h b/dataflow_cpp/include/embb/dataflow/internal/node.h index 8aa2805..d4486b1 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/node.h +++ b/dataflow_cpp/include/embb/dataflow/internal/node.h @@ -45,6 +45,7 @@ class Node { virtual void Run(int clock) = 0; virtual bool IsFullyConnected() = 0; virtual bool IsSequential() { return true; } + virtual bool HasCycle() { return false; } virtual bool Start(int /*clock*/) { EMBB_THROW(embb::base::ErrorException, "Nodes are started implicitly."); diff --git a/dataflow_cpp/include/embb/dataflow/internal/out.h b/dataflow_cpp/include/embb/dataflow/internal/out.h index 3f5aea4..72ff3c8 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/out.h +++ b/dataflow_cpp/include/embb/dataflow/internal/out.h @@ -36,6 +36,7 @@ namespace dataflow { namespace internal { class Scheduler; +class ClockListener; template class Out { @@ -70,6 +71,14 @@ class Out { return targets_.size() > 0; } + bool HasCycle(ClockListener * node) { + bool result = false; + for (size_t ii = 0; ii < targets_.size() && !result; ii++) { + result = result || targets_[ii]->HasCycle(node); + } + return result; + } + private: std::vector< InType * > targets_; }; diff --git a/dataflow_cpp/include/embb/dataflow/internal/outputs.h b/dataflow_cpp/include/embb/dataflow/internal/outputs.h index 2fce315..f969267 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/outputs.h +++ b/dataflow_cpp/include/embb/dataflow/internal/outputs.h @@ -42,6 +42,8 @@ template < typename = embb::base::internal::Nil > class Outputs; +class ClockListener; + template <> class Outputs @@ -65,6 +70,9 @@ class Outputstemplate Get<0>().IsConnected(); } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node); + } }; template @@ -77,6 +85,10 @@ class Outputstemplate Get<0>().IsConnected() && this->template Get<1>().IsConnected(); } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node); + } }; template @@ -85,6 +97,16 @@ class Outputs, Out, Out, embb::base::internal::Nil, embb::base::internal::Nil> { public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected() && + this->template Get<2>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node) || + this->template Get<2>().HasCycle(node); + } }; template @@ -92,6 +114,18 @@ class Outputs : public Tuple, Out, Out, Out, embb::base::internal::Nil>{ public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected() && + this->template Get<2>().IsConnected() && + this->template Get<3>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node) || + this->template Get<2>().HasCycle(node) || + this->template Get<3>().HasCycle(node); + } }; template , Out, Out, Out, Out > { public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected() && + this->template Get<2>().IsConnected() && + this->template Get<3>().IsConnected() && + this->template Get<4>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node) || + this->template Get<2>().HasCycle(node) || + this->template Get<3>().HasCycle(node) || + this->template Get<4>().HasCycle(node); + } }; } // namespace internal diff --git a/dataflow_cpp/include/embb/dataflow/internal/process.h b/dataflow_cpp/include/embb/dataflow/internal/process.h index b728d81..7f0f538 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/process.h +++ b/dataflow_cpp/include/embb/dataflow/internal/process.h @@ -96,6 +96,10 @@ class Process< Serial, Inputs, return Serial; } + virtual bool HasCycle() { + return outputs_.HasCycle(this); + } + InputsType & GetInputs() { return inputs_; } @@ -158,6 +162,15 @@ class Process< Serial, Inputs, } } + virtual bool OnHasCycle(ClockListener * node) { + ClockListener * this_node = this; + if (this_node == node) { + return true; + } else { + return outputs_.HasCycle(node); + } + } + private: InputsType inputs_; OutputsType outputs_; diff --git a/dataflow_cpp/include/embb/dataflow/internal/select.h b/dataflow_cpp/include/embb/dataflow/internal/select.h index 40d6b6f..ab4a1aa 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/select.h +++ b/dataflow_cpp/include/embb/dataflow/internal/select.h @@ -85,6 +85,15 @@ class Select return inputs_.IsFullyConnected() && outputs_.IsFullyConnected(); } + virtual bool OnHasCycle(ClockListener * node) { + ClockListener * this_node = this; + if (this_node == node) { + return true; + } else { + return outputs_.HasCycle(node); + } + } + InputsType & GetInputs() { return inputs_; } diff --git a/dataflow_cpp/include/embb/dataflow/internal/switch.h b/dataflow_cpp/include/embb/dataflow/internal/switch.h index 788685b..9abe904 100644 --- a/dataflow_cpp/include/embb/dataflow/internal/switch.h +++ b/dataflow_cpp/include/embb/dataflow/internal/switch.h @@ -82,6 +82,15 @@ class Switch return inputs_.IsFullyConnected() && outputs_.IsFullyConnected(); } + virtual bool OnHasCycle(ClockListener * node) { + ClockListener * this_node = this; + if (this_node == node) { + return true; + } else { + return outputs_.HasCycle(node); + } + } + InputsType & GetInputs() { return inputs_; } diff --git a/dataflow_cpp/include/embb/dataflow/network.h b/dataflow_cpp/include/embb/dataflow/network.h index d4039c6..4658bbb 100644 --- a/dataflow_cpp/include/embb/dataflow/network.h +++ b/dataflow_cpp/include/embb/dataflow/network.h @@ -676,6 +676,8 @@ class Network { /** * Checks whether the network is completely connected and free of cycles. * \returns \c true if everything is in order, \c false if not. + * \note Executing an invalid network results in an exception. For this + * reason, it is recommended to first check the network using IsValid(). */ bool IsValid(); @@ -685,6 +687,8 @@ class Network { * tokens will automatically be derived from the structure of the network * on the first call of the operator, and the corresponding resources will * be allocated then. + * \note Executing an invalid network results in an exception. For this + * reason, it is recommended to first check the network using IsValid(). */ void operator () (); }; @@ -846,14 +850,19 @@ class Network : public internal::ClockListener { bool IsValid() { bool valid = true; - for (size_t ii = 0; ii < sources_.size(); ii++) { - valid = valid & sources_[ii]->IsFullyConnected(); + // check connectivity + for (size_t ii = 0; ii < sources_.size() && valid; ii++) { + valid = valid && sources_[ii]->IsFullyConnected(); } - for (size_t ii = 0; ii < processes_.size(); ii++) { - valid = valid & processes_[ii]->IsFullyConnected(); + for (size_t ii = 0; ii < processes_.size() && valid; ii++) { + valid = valid && processes_[ii]->IsFullyConnected(); } - for (size_t ii = 0; ii < sinks_.size(); ii++) { - valid = valid & sinks_[ii]->IsFullyConnected(); + for (size_t ii = 0; ii < sinks_.size() && valid; ii++) { + valid = valid && sinks_[ii]->IsFullyConnected(); + } + // check for cycles + for (size_t ii = 0; ii < processes_.size() && valid; ii++) { + valid = valid && !processes_[ii]->HasCycle(); } return valid; }