aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2023-02-22 13:31:34 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-02-23 12:11:59 +0000
commitb7ed7e9a815c4e10447fd499508be3dbb47f06e8 (patch)
tree3aad2c062555adb34de81272b5a981ccd5507172
parent36a231d2a45e7c87172196538d18ded81a234774 (diff)
downloadbitbake-contrib-b7ed7e9a815c4e10447fd499508be3dbb47f06e8.tar.gz
event/cooker/runqueue: Add ability to interrupt longer running code
Bitbake is now able to understand when a UI wants it to stop the current processing. There are some long running functions which currently have no mechanism to interrupt them however. This patch adds a call, bb.event.check_for_interrupts(d) which can be placed in long running code and allows an internal state flag within bitbake to be checked. If set, that flag will trigger an exit. This means that Ctrl+C can be made to work in a variety of places where it currently would not. Long running event handlers in OE-Core can also then benefit from this new approach with the addition of the function call as well. Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--lib/bb/cooker.py4
-rw-r--r--lib/bb/event.py11
-rw-r--r--lib/bb/runqueue.py15
3 files changed, 30 insertions, 0 deletions
diff --git a/lib/bb/cooker.py b/lib/bb/cooker.py
index b673fe10e..c631ec7e6 100644
--- a/lib/bb/cooker.py
+++ b/lib/bb/cooker.py
@@ -345,6 +345,7 @@ class BBCooker:
elif signum == signal.SIGHUP:
bb.warn("Cooker received SIGHUP, shutting down...")
self.state = state.forceshutdown
+ bb.event._should_exit.set()
def setFeatures(self, features):
# we only accept a new feature set if we're in state initial, so we can reset without problems
@@ -1520,6 +1521,7 @@ class BBCooker:
msg = None
interrupted = 0
if halt or self.state == state.forceshutdown:
+ bb.event._should_exit.set()
rq.finish_runqueue(True)
msg = "Forced shutdown"
interrupted = 2
@@ -1760,6 +1762,7 @@ class BBCooker:
self.state = state.forceshutdown
else:
self.state = state.shutdown
+ bb.event._should_exit.set()
if self.parser:
self.parser.shutdown(clean=False)
@@ -1770,6 +1773,7 @@ class BBCooker:
self.parser.shutdown(clean=False)
self.parser.final_cleanup()
self.state = state.initial
+ bb.event._should_exit.clear()
def reset(self):
if hasattr(bb.parse, "siggen"):
diff --git a/lib/bb/event.py b/lib/bb/event.py
index 8b05f93e2..37cc630c6 100644
--- a/lib/bb/event.py
+++ b/lib/bb/event.py
@@ -69,6 +69,7 @@ _eventfilter = None
_uiready = False
_thread_lock = threading.Lock()
_heartbeat_enabled = False
+_should_exit = threading.Event()
def enable_threadlock():
# Always needed now
@@ -86,6 +87,16 @@ def disable_heartbeat():
global _heartbeat_enabled
_heartbeat_enabled = False
+#
+# In long running code, this function should be called periodically
+# to check if we should exit due to an interuption (.e.g Ctrl+C from the UI)
+#
+def check_for_interrupts(d):
+ global _should_exit
+ if _should_exit.is_set():
+ bb.warn("Exiting due to interrupt.")
+ raise bb.BBHandledException()
+
def execute_handler(name, handler, event, d):
event.data = d
try:
diff --git a/lib/bb/runqueue.py b/lib/bb/runqueue.py
index e5bd9311f..e629ab7e7 100644
--- a/lib/bb/runqueue.py
+++ b/lib/bb/runqueue.py
@@ -655,6 +655,7 @@ class RunQueueData:
self.init_progress_reporter.start()
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Step A - Work out a list of tasks to run
#
@@ -803,6 +804,7 @@ class RunQueueData:
#self.dump_data()
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Resolve recursive 'recrdeptask' dependencies (Part B)
#
@@ -899,6 +901,7 @@ class RunQueueData:
self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
#self.dump_data()
@@ -980,6 +983,7 @@ class RunQueueData:
mark_active(tid, 1)
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Step C - Prune all inactive tasks
#
@@ -1019,6 +1023,7 @@ class RunQueueData:
bb.msg.fatal("RunQueue", "Could not find any tasks with the tasknames %s to run within the recipes of the taskgraphs of the targets %s" % (str(self.cooker.configuration.runall), str(self.targets)))
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Handle runonly
if self.cooker.configuration.runonly:
@@ -1059,6 +1064,7 @@ class RunQueueData:
logger.verbose("Assign Weightings")
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Generate a list of reverse dependencies to ease future calculations
for tid in self.runtaskentries:
@@ -1066,6 +1072,7 @@ class RunQueueData:
self.runtaskentries[dep].revdeps.add(tid)
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Identify tasks at the end of dependency chains
# Error on circular dependency loops (length two)
@@ -1082,12 +1089,14 @@ class RunQueueData:
logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Calculate task weights
# Check of higher length circular dependencies
self.runq_weight = self.calculate_task_weights(endpoints)
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Sanity Check - Check for multiple tasks building the same provider
for mc in self.dataCaches:
@@ -1188,6 +1197,7 @@ class RunQueueData:
self.init_progress_reporter.next_stage()
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Iterate over the task list looking for tasks with a 'setscene' function
self.runq_setscene_tids = set()
@@ -1200,6 +1210,7 @@ class RunQueueData:
self.runq_setscene_tids.add(tid)
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Invalidate task if force mode active
if self.cooker.configuration.force:
@@ -1216,6 +1227,7 @@ class RunQueueData:
invalidate_task(fn + ":" + st, True)
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
# Create and print to the logs a virtual/xxxx -> PN (fn) table
for mc in taskData:
@@ -1228,6 +1240,7 @@ class RunQueueData:
bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
self.init_progress_reporter.next_stage()
+ bb.event.check_for_interrupts(self.cooker.data)
bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
@@ -1240,6 +1253,7 @@ class RunQueueData:
dealtwith.add(tid)
todeal.remove(tid)
self.prepare_task_hash(tid)
+ bb.event.check_for_interrupts(self.cooker.data)
bb.parse.siggen.writeout_file_checksum_cache()
@@ -1483,6 +1497,7 @@ class RunQueue:
"""
retval = True
+ bb.event.check_for_interrupts(self.cooker.data)
if self.state is runQueuePrepare:
# NOTE: if you add, remove or significantly refactor the stages of this