summaryrefslogtreecommitdiff
path: root/src/buildstream/_state.py
diff options
context:
space:
mode:
authorJonathan Maw <jonathan.maw@codethink.co.uk>2019-06-10 12:47:32 +0100
committerbst-marge-bot <marge-bot@buildstream.build>2019-07-09 16:54:37 +0000
commit0379151c1185de654c3f3672fbec5c16ee7f49a6 (patch)
tree0d002654ef5dc555b3704b3cdeeeff18d7febbbd /src/buildstream/_state.py
parent478d7889ef8ffa927f7ba412bd5dd374032c381d (diff)
downloadbuildstream-0379151c1185de654c3f3672fbec5c16ee7f49a6.tar.gz
Store core state for the frontend separately
Diffstat (limited to 'src/buildstream/_state.py')
-rw-r--r--src/buildstream/_state.py310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/buildstream/_state.py b/src/buildstream/_state.py
new file mode 100644
index 000000000..b2f0b705d
--- /dev/null
+++ b/src/buildstream/_state.py
@@ -0,0 +1,310 @@
+#
+# Copyright (C) 2019 Bloomberg Finance LP
+#
+# 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/>.
+#
+
+
+from collections import OrderedDict
+
+
+# TaskGroup
+#
+# The state data stored for a group of tasks (usually scheduler queues)
+#
+# Args:
+# name (str): The name of the Task Group, e.g. 'build'
+# state (State): The state object
+#
+class TaskGroup():
+ def __init__(self, name, state):
+ self.name = name
+ self.processed_tasks = 0
+ self.skipped_tasks = 0
+ # NOTE: failed_tasks is a list of strings instead of an integer count
+ # because the frontend requires the full list of failed tasks to
+ # know whether to print failure messages for a given element.
+ self.failed_tasks = []
+
+ self._state = state
+ self._update_task_group_cbs = []
+
+ ###########################################
+ # Core-facing APIs to drive notifications #
+ ###########################################
+
+ # add_processed_task()
+ #
+ # Update the TaskGroup's count of processed tasks and notify of changes
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ def add_processed_task(self):
+ self.processed_tasks += 1
+ for cb in self._state._task_groups_changed_cbs:
+ cb()
+
+ # add_skipped_task()
+ #
+ # Update the TaskGroup's count of skipped tasks and notify of changes
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ def add_skipped_task(self):
+ self.skipped_tasks += 1
+
+ for cb in self._state._task_groups_changed_cbs:
+ cb()
+
+ # add_failed_task()
+ #
+ # Update the TaskGroup's list of failed tasks and notify of changes
+ #
+ # Args:
+ # full_name (str): The full name of the task, distinguishing
+ # it from other tasks with the same action name
+ # e.g. an element's name.
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ def add_failed_task(self, full_name):
+ self.failed_tasks.append(full_name)
+
+ for cb in self._state._task_groups_changed_cbs:
+ cb()
+
+
+# State
+#
+# The state data that is stored for the purpose of sharing with the frontend.
+#
+# BuildStream's Core is responsible for making changes to this data.
+# BuildStream's Frontend may register callbacks with State to be notified
+# when parts of State change, and read State to know what has changed.
+class State():
+ def __init__(self):
+ self.task_groups = OrderedDict() # key is TaskGroup name
+
+ # Note: A Task's full_name is technically unique, but only accidentally.
+ self.tasks = OrderedDict() # key is a tuple of action_name and full_name
+
+ self._task_added_cbs = []
+ self._task_removed_cbs = []
+ self._task_groups_changed_cbs = []
+ self._task_failed_cbs = []
+
+ #####################################
+ # Frontend-facing notification APIs #
+ #####################################
+
+ # register_task_added_callback()
+ #
+ # Registers a callback to be notified when a task is added
+ #
+ # Args:
+ # callback (function): The callback to be notified
+ #
+ # Callback Args:
+ # action_name (str): The name of the action, e.g. 'build'
+ # full_name (str): The full name of the task, distinguishing
+ # it from other tasks with the same action name
+ # e.g. an element's name.
+ #
+ def register_task_added_callback(self, callback):
+ self._task_added_cbs.append(callback)
+
+ # unregister_task_added_callback()
+ #
+ # Unregisters a callback previously registered by
+ # register_task_added_callback()
+ #
+ # Args:
+ # callback (function): The callback to be removed
+ #
+ def unregister_task_added_callback(self, callback):
+ self._task_added_cbs.remove(callback)
+
+ # register_task_removed_callback()
+ #
+ # Registers a callback to be notified when a task is removed
+ #
+ # Args:
+ # callback (function): The callback to be notified
+ #
+ # Callback Args:
+ # action_name (str): The name of the action, e.g. 'build'
+ # full_name (str): The full name of the task, distinguishing
+ # it from other tasks with the same action name
+ # e.g. an element's name.
+ #
+ def register_task_removed_callback(self, callback):
+ self._task_removed_cbs.append(callback)
+
+ # unregister_task_removed_callback()
+ #
+ # Unregisters a callback previously registered by
+ # register_task_removed_callback()
+ #
+ # Args:
+ # callback (function): The callback to be notified
+ #
+ def unregister_task_removed_callback(self, callback):
+ self._task_removed_cbs.remove(callback)
+
+ # register_task_failed_callback()
+ #
+ # Registers a callback to be notified when a task has failed
+ #
+ # Args:
+ # callback (function): The callback to be notified
+ #
+ # Callback Args:
+ # action_name (str): The name of the action, e.g. 'build'
+ # full_name (str): The full name of the task, distinguishing
+ # it from other tasks with the same action name
+ # e.g. an element's name.
+ # unique_id (int): (optionally) the element's unique ID, if the failure
+ # came from an element
+ #
+ def register_task_failed_callback(self, callback):
+ self._task_failed_cbs.append(callback)
+
+ # unregister_task_failed_callback()
+ #
+ # Unregisters a callback previously registered by
+ # register_task_failed_callback()
+ #
+ # Args:
+ # callback (function): The callback to be removed
+ #
+ def unregister_task_failed_callback(self, callback):
+ self._task_failed_cbs.remove(callback)
+
+ ##############################################
+ # Core-facing APIs for driving notifications #
+ ##############################################
+
+ # add_task_group()
+ #
+ # Notification that a new task group has been added
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ # Args:
+ # name (str): The name of the task group, e.g. 'build'
+ #
+ # Returns:
+ # TaskGroup: The task group created
+ #
+ def add_task_group(self, name):
+ assert name not in self.task_groups, "Trying to add task group '{}' to '{}'".format(name, self.task_groups)
+ group = TaskGroup(name, self)
+ self.task_groups[name] = group
+
+ return group
+
+ # remove_task_group()
+ #
+ # Notification that a task group has been removed
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ # Args:
+ # name (str): The name of the task group, e.g. 'build'
+ #
+ def remove_task_group(self, name):
+ # Rely on 'del' to raise an error when removing nonexistent task groups
+ del self.task_groups[name]
+
+ # add_task()
+ #
+ # Add a task and send appropriate notifications
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ # Args:
+ # action_name (str): The name of the action, e.g. 'build'
+ # full_name (str): The full name of the task, distinguishing
+ # it from other tasks with the same action name
+ # e.g. an element's name.
+ # start_time (timedelta): The time the task started, relative to
+ # buildstream's start time.
+ #
+ def add_task(self, action_name, full_name, start_time):
+ task_key = (action_name, full_name)
+ assert task_key not in self.tasks, \
+ "Trying to add task '{}:{}' to '{}'".format(action_name, full_name, self.tasks)
+
+ task = _Task(action_name, full_name, start_time)
+ self.tasks[task_key] = task
+
+ for cb in self._task_added_cbs:
+ cb(action_name, full_name)
+
+ # remove_task()
+ #
+ # Remove the task and send appropriate notifications
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ # Args:
+ # action_name (str): The name of the action, e.g. 'build'
+ # full_name (str): The full name of the task, distinguishing
+ # it from other tasks with the same action name
+ # e.g. an element's name.
+ #
+ def remove_task(self, action_name, full_name):
+ # Rely on 'del' to raise an error when removing nonexistent tasks
+ del self.tasks[(action_name, full_name)]
+
+ for cb in self._task_removed_cbs:
+ cb(action_name, full_name)
+
+ # fail_task()
+ #
+ # Notify all registered callbacks that a task has failed.
+ #
+ # This is separate from the tasks changed callbacks because a failed task
+ # requires the frontend to intervene to decide what happens next.
+ #
+ # This is a core-facing API and should not be called from the frontend
+ #
+ # Args:
+ # action_name (str): The name of the action, e.g. 'build'
+ # full_name (str): The full name of the task, distinguishing
+ # it from other tasks with the same action name
+ # e.g. an element's name.
+ # unique_id (int): (optionally) the element's unique ID, if the failure came from an element
+ #
+ def fail_task(self, action_name, full_name, unique_id=None):
+ for cb in self._task_failed_cbs:
+ cb(action_name, full_name, unique_id)
+
+
+# _Task
+#
+# The state data stored for an individual task
+#
+# Args:
+# action_name (str): The name of the action, e.g. 'build'
+# full_name (str): The full name of the task, distinguishing
+# it from other tasks with the same action name
+# e.g. an element's name.
+# start_time (timedelta): The time the task started, relative to
+# buildstream's start time.
+class _Task():
+ def __init__(self, action_name, full_name, start_time):
+ self.action_name = action_name
+ self.full_name = full_name
+ self.start_time = start_time