diff --git a/src/scheduler/Scheduler.cpp b/src/scheduler/Scheduler.cpp
index 6f9dea3ae0b5d27e3fdea36adc0478deeb815a05..08a7c274b40f181f147ed1b97c79a465153ab1d6 100644
--- a/src/scheduler/Scheduler.cpp
+++ b/src/scheduler/Scheduler.cpp
@@ -45,8 +45,8 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
     // TODO: handle memory allocation in scheduler
     // TODO: optimize memory usage
 
-    // Setup initial potential consumers list:
-    // List of input nodes
+    // 1) Setup initial consumers list:
+    // It is the list of input nodes
     std::set<std::shared_ptr<Node>> consumers = mGraphView->inputNodes();
     // Plus the list of nodes inside the graph connected to an inner producer
     std::set<std::shared_ptr<Node>> producers;
@@ -58,14 +58,6 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
     const auto producersConsumers = getConsumers(producers);
     consumers.insert(producersConsumers.begin(), producersConsumers.end());
 
-    // Frozen consumers is used as a stop condition of the scheduling loop:
-    // The first time no consumer is runnable, frozenConsumers is updated to the
-    // current list of consumer. If after successive iterations, all with no
-    // runnable consumer, the list of consumer is again equal to frozenConsumers
-    // it means we are in cycle with no more scheduling update, a.k.a. a
-    // frozen state.
-    std::vector<std::set<std::shared_ptr<Node>>> frozenConsumers;
-
     std::map<std::shared_ptr<Node>, std::string> namePtrTable;
     if (verbose) namePtrTable = mGraphView->getRankedNodesName("{0} ({1}#{3})");
 
@@ -77,14 +69,16 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
     mStaticSchedule.push_back(std::vector<std::shared_ptr<Node>>());
 
     do {
-        // From the current consumers list, check if any prior nodes are needed.
+        // 2) From the current consumers list, check if any prior consumer node
+        // is needed. A prior will generally be required for any node consuming 
+        // parameters (weights and bias) that is not an input node.
         // If for a given node, only parent producers (at any depth) are needed
         // to satisfy its required data, it becomes a prior.
         // If the prior node is a producer, it is added to the list of required
         // producers.
         // If the prior node is of another type, it replaces the initial consumer
-        // in the new priorConsumers list. The initial consumer will necessarily
-        // be added again later in the consumers list.
+        // in the new priorConsumers list. The initial consumer will become 
+        // again a consumer later, by construction.
         if (verbose) printf("List of consumers with their priors:\n");
         std::set<std::shared_ptr<Node>> requiredProducers;
         std::set<std::shared_ptr<Node>> priorConsumers;
@@ -124,15 +118,23 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
             }
         }
 
+        // 3) Prior consumers replace the initial consumers list.
+        // By construction, initial consumers will necessarily become consumers
+        // again later.
         consumers.swap(priorConsumers);
 
-        // Make producers generate the required data
+        // 4) Make producers generate the required data.
+        // Producers are special nodes that generate data on demand.
         for (const auto& requiredProducer : requiredProducers) {
             requiredProducer->getOperator()->updateConsummerProducer();
             mStaticSchedule.back().push_back(requiredProducer);
         }
 
-        // find runnable consumers
+        // 5) Find runnable consumers.
+        // A consumer is runnable if the required data is available for all of 
+        // its inputs. At this point, not all consumers are necessarily
+        // runnable because some may depend on the execution of others (when
+        // there is multiple successive priors for example).
         std::set<std::shared_ptr<Node>> runnableConsumers;
         if (verbose) printf("Updated list of consumers:\n");
         for (const auto& consumer : consumers) {
@@ -178,30 +180,31 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
             }
         }
 
-        // Push consumers in the list of nodes to run and update the consumer producer system
+        // 5) If not consumer is runnable, it is a stop condition!
+        if (runnableConsumers.empty()) {
+            if (verbose) printf("********************\n");
+            // No consumer is runnable: some required data is missing for all of
+            // them. There is two possibilities:
+            // - At least one required data source is exhausted, which may be
+            //   an expected stop condition.
+            // - There is a deadlock between consumers, if some one is waiting
+            //   for data from the other and reciprocally.
+            break;
+        }
+
+        // 6) Push runnable consumers in the list of nodes to run and update the
+        // consumer producer system.
+        // At this point, simultaneously runnable consumers have no data 
+        // dependency and could be run in parallel!
         for (const auto& runnable : runnableConsumers) {
             if (verbose) printf("Runnable: %s\n", namePtrTable[runnable].c_str());
             runnable->getOperator()->updateConsummerProducer();
             mStaticSchedule.back().push_back(runnable);
         }
 
-        if (runnableConsumers.empty()) {
-            if (std::find(frozenConsumers.begin(), frozenConsumers.end(), consumers) == frozenConsumers.end()) {
-                frozenConsumers.push_back(consumers);
-            }
-            else {
-                break;
-            }
-        }
-        else {
-            frozenConsumers.clear();
-        }
-
-        // update producers and consumers list
+        // 7) Update consumers list
         if (verbose) printf("Updating producer and consumer lists...\n");
-        const auto oldConsumers = consumers;
-
-        for (const auto& consumer : oldConsumers) {
+        for (const auto& consumer : runnableConsumers) {
             if (verbose) {
                 printf("\t- consumer: %s\n\t\tC/R:\t",
                        namePtrTable[consumer].c_str());
@@ -219,6 +222,9 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
                 printf("\n");
             }
 
+            // 7.1) If the current consumer has still data to consume, it will
+            // be put back in the consumers list once the remaining consumers
+            // have been exhausted.
             bool isStillConsumer = false;
             for (IOIndex_t inputIdx = 0; inputIdx < consumer->nbInputs(); ++inputIdx) {
                 if (consumer->getOperator()->getNbConsumedData(inputIdx) <
@@ -233,7 +239,24 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
                 }
             }
 
+            // 7.2) If the current consumer becomes a producer for other nodes,
+            // its childs become consumers.
+            bool isProducer = false;
             for (IOIndex_t outId = 0; outId < consumer->nbOutputs(); ++outId) {
+                for (const auto child : consumer->getChildren(outId)) {
+                    if (child) {
+                        IOIndex_t inputIdx = 0;
+                        for (const auto childParent : child->getParents()) {
+                            if (childParent == consumer) {
+                                if (consumer->getOperator()->getNbProducedData(outId) > child->getOperator()->getNbConsumedData(inputIdx)) {
+                                    isProducer = true;
+                                }
+                            }
+                            ++inputIdx;
+                        }
+                    }
+                }
+/*
                 if (consumer->getOperator()->getNbProducedData(outId) > 0) {
                     if (verbose) printf("  also producer\n");
                     // make sure consumer is also a producer
@@ -243,22 +266,29 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
                     consumers.insert(newConsumers.cbegin(), newConsumers.cend());
                     break;
                 }
+*/
             }
 
-            if (runnableConsumers.find(consumer) != runnableConsumers.end()) {
-                // If consumer was run, remove it from the consumers list for
-                // now
-                consumers.erase(consumer);
-                if (isStillConsumer) {
-                    // If there is still data to consume, the consumer will be
-                    // run AFTER the other remaining consumers
-                    // (= non-greedy consumers)
-                    stillConsumers.insert(consumer);
-                }
+            consumers.erase(consumer);
+
+            if (isProducer) {
+                if (verbose) printf("  also producer\n");
+                // make sure consumer is also a producer
+                producers.insert(consumer);
+
+                const auto& newConsumers = getConsumers({consumer});
+                consumers.insert(newConsumers.cbegin(), newConsumers.cend());
+            }
+
+            if (isStillConsumer) {
+                // If there is still data to consume, the consumer will be
+                // run AFTER the other remaining consumers
+                // (= non-greedy consumers)
+                stillConsumers.insert(consumer);
             }
         }
 
-        // If there is no more consumers, swap with possible "still consumers"
+        // 8) If there is no more consumers, swap with possible "still consumers"
         // This ensures that the "non-greedy" consumer behavior
         if (consumers.empty()) {
             consumers.swap(stillConsumers);
@@ -270,7 +300,7 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
 
     if (verbose) {
         if (!consumers.empty()) {
-            printf("*** Frozen state ***\n");
+            printf("/!\\ Remaining consumers: possible dead-lock\n");
             printf("********************\n");
         }
     }