You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildstream.apache.org by tv...@apache.org on 2021/02/04 07:50:49 UTC

[buildstream] 03/04: buildstream/_scheduler/*: Add QueueRunner

This is an automated email from the ASF dual-hosted git repository.

tvb pushed a commit to branch 372-allow-queues-to-run-auxilliary-jobs-after-an-element-s-job-finishes
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 8e59ca0b205b5f84e1e9a88cd169bd4168a53ded
Author: Tristan Maat <tr...@codethink.co.uk>
AuthorDate: Fri Apr 20 15:35:25 2018 +0100

    buildstream/_scheduler/*: Add QueueRunner
---
 buildstream/_scheduler/queuerunner.py | 104 ++++++++++++++++++++++++++++++++++
 buildstream/_scheduler/scheduler.py   |  35 ++----------
 2 files changed, 109 insertions(+), 30 deletions(-)

diff --git a/buildstream/_scheduler/queuerunner.py b/buildstream/_scheduler/queuerunner.py
new file mode 100644
index 0000000..aac2e40
--- /dev/null
+++ b/buildstream/_scheduler/queuerunner.py
@@ -0,0 +1,104 @@
+#  Copyright (C) 2018 Codethink Limited
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+#
+#  Author:
+#        Tristan Daniƫl Maat <tr...@codethink.co.uk>
+#
+from itertools import chain
+
+
+# QueueRunner()
+#
+# The queue runner manages queues and determines what jobs should be
+# run at any given point.
+#
+# `QueueRunner.schedule_jobs` will pull elements through queues as
+# appropriate, and return jobs for any "ready" elements to submit for
+# processing.
+#
+# Args:
+#     scheduler (Scheduler) - The scheduler to provide jobs for.
+#     queues ([Queue]) - The queues to manage.
+#
+class QueueRunner():
+    def __init__(self, scheduler, queues=None):
+        try:
+            queues = list(queues)
+        except TypeError:
+            queues = [queues] if queues is not None else []
+
+        self.queues = queues
+        self._scheduler = scheduler
+
+    # append()
+    #
+    # Append a queue to the list of queues.
+    #
+    # Args:
+    #     queue (Queue) - The queue to append
+    #
+    def append(self, queue):
+        self.queues.append(queue)
+
+    # extend()
+    #
+    # Extend the list of queues.
+    #
+    # Args:
+    #    queues (typing.Iterable[Queue]) - The queues to append
+    #
+    def extend(self, queues):
+        self.queues.extend(queues)
+
+    # schedule_jobs()
+    #
+    # Pull elements through the managed queues, and collect jobs to be
+    # executed by the scheduler
+    #
+    # Returns:
+    #    (typing.List[Job]) jobs for the scheduler to execute.
+    #
+    def schedule_jobs(self):
+
+        ready = []
+        process_queues = True
+
+        while self._scheduler._queue_jobs and process_queues:
+
+            # Pull elements forward through queues
+            elements = []
+            for queue in self.queues:
+                # Enqueue elements complete from the last queue
+                queue.enqueue(elements)
+
+                # Dequeue processed elements for the next queue
+                elements = list(queue.dequeue())
+
+            # Kickoff whatever processes can be processed at this time
+            #
+            # We start by queuing from the last queue first, because we want to
+            # give priority to queues later in the scheduling process in the case
+            # that multiple queues share the same token type.
+            #
+            # This avoids starvation situations where we dont move on to fetch
+            # tasks for elements which failed to pull, and thus need all the pulls
+            # to complete before ever starting a build
+            ready.extend(chain.from_iterable(queue.process_ready() for queue in reversed(self.queues)))
+
+            # process_ready() may have skipped jobs, adding them to the done_queue.
+            # Pull these skipped elements forward to the next queue and process them.
+            process_queues = any(q.dequeue_ready() for q in self.queues)
+
+        return ready
diff --git a/buildstream/_scheduler/scheduler.py b/buildstream/_scheduler/scheduler.py
index a475722..61cfc11 100644
--- a/buildstream/_scheduler/scheduler.py
+++ b/buildstream/_scheduler/scheduler.py
@@ -28,6 +28,7 @@ from contextlib import contextmanager
 
 # Local imports
 from .queues import QueueType
+from .queuerunner import QueueRunner
 
 
 # A decent return code for Scheduler.run()
@@ -82,6 +83,7 @@ class Scheduler():
         #
         # Private members
         #
+        self._runners = []
         self._interrupt_callback = interrupt_callback
         self._ticker_callback = ticker_callback
         self._job_start_callback = job_start_callback
@@ -115,6 +117,7 @@ class Scheduler():
     def run(self, queues):
 
         # Hold on to the queues to process
+        self._runners.append(QueueRunner(self, queues))
         self.queues = queues
 
         # Ensure that we have a fresh new event loop, in case we want
@@ -221,36 +224,8 @@ class Scheduler():
     # and process anything that is ready.
     #
     def sched(self):
-
-        process_queues = True
-
-        while self._queue_jobs and process_queues:
-
-            # Pull elements forward through queues
-            elements = []
-            for queue in self.queues:
-                # Enqueue elements complete from the last queue
-                queue.enqueue(elements)
-
-                # Dequeue processed elements for the next queue
-                elements = list(queue.dequeue())
-                elements = list(elements)
-
-            # Kickoff whatever processes can be processed at this time
-            #
-            # We start by queuing from the last queue first, because we want to
-            # give priority to queues later in the scheduling process in the case
-            # that multiple queues share the same token type.
-            #
-            # This avoids starvation situations where we dont move on to fetch
-            # tasks for elements which failed to pull, and thus need all the pulls
-            # to complete before ever starting a build
-            for queue in reversed(self.queues):
-                queue.process_ready()
-
-            # process_ready() may have skipped jobs, adding them to the done_queue.
-            # Pull these skipped elements forward to the next queue and process them.
-            process_queues = any(q.dequeue_ready() for q in self.queues)
+        for runner in self._runners:
+            runner.schedule_jobs()
 
         # If nothings ticking, time to bail out
         ticking = 0