1
0
mirror of https://git.yoctoproject.org/poky synced 2026-05-31 12:49:46 +00:00

bitbake: 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.

(Bitbake rev: b7ed7e9a815c4e10447fd499508be3dbb47f06e8)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Richard Purdie
2023-02-22 13:31:34 +00:00
parent 906657e9be
commit 42c92cbc83
3 changed files with 30 additions and 0 deletions
+4
View File
@@ -345,6 +345,7 @@ class BBCooker:
elif signum == signal.SIGHUP: elif signum == signal.SIGHUP:
bb.warn("Cooker received SIGHUP, shutting down...") bb.warn("Cooker received SIGHUP, shutting down...")
self.state = state.forceshutdown self.state = state.forceshutdown
bb.event._should_exit.set()
def setFeatures(self, features): def setFeatures(self, features):
# we only accept a new feature set if we're in state initial, so we can reset without problems # 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 msg = None
interrupted = 0 interrupted = 0
if halt or self.state == state.forceshutdown: if halt or self.state == state.forceshutdown:
bb.event._should_exit.set()
rq.finish_runqueue(True) rq.finish_runqueue(True)
msg = "Forced shutdown" msg = "Forced shutdown"
interrupted = 2 interrupted = 2
@@ -1760,6 +1762,7 @@ class BBCooker:
self.state = state.forceshutdown self.state = state.forceshutdown
else: else:
self.state = state.shutdown self.state = state.shutdown
bb.event._should_exit.set()
if self.parser: if self.parser:
self.parser.shutdown(clean=False) self.parser.shutdown(clean=False)
@@ -1770,6 +1773,7 @@ class BBCooker:
self.parser.shutdown(clean=False) self.parser.shutdown(clean=False)
self.parser.final_cleanup() self.parser.final_cleanup()
self.state = state.initial self.state = state.initial
bb.event._should_exit.clear()
def reset(self): def reset(self):
if hasattr(bb.parse, "siggen"): if hasattr(bb.parse, "siggen"):
+11
View File
@@ -69,6 +69,7 @@ _eventfilter = None
_uiready = False _uiready = False
_thread_lock = threading.Lock() _thread_lock = threading.Lock()
_heartbeat_enabled = False _heartbeat_enabled = False
_should_exit = threading.Event()
def enable_threadlock(): def enable_threadlock():
# Always needed now # Always needed now
@@ -86,6 +87,16 @@ def disable_heartbeat():
global _heartbeat_enabled global _heartbeat_enabled
_heartbeat_enabled = False _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): def execute_handler(name, handler, event, d):
event.data = d event.data = d
try: try:
+15
View File
@@ -655,6 +655,7 @@ class RunQueueData:
self.init_progress_reporter.start() self.init_progress_reporter.start()
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Step A - Work out a list of tasks to run # Step A - Work out a list of tasks to run
# #
@@ -803,6 +804,7 @@ class RunQueueData:
#self.dump_data() #self.dump_data()
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Resolve recursive 'recrdeptask' dependencies (Part B) # Resolve recursive 'recrdeptask' dependencies (Part B)
# #
@@ -899,6 +901,7 @@ class RunQueueData:
self.runtaskentries[tid].depends.difference_update(recursivetasksselfref) self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
#self.dump_data() #self.dump_data()
@@ -980,6 +983,7 @@ class RunQueueData:
mark_active(tid, 1) mark_active(tid, 1)
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Step C - Prune all inactive tasks # 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))) 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() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Handle runonly # Handle runonly
if self.cooker.configuration.runonly: if self.cooker.configuration.runonly:
@@ -1059,6 +1064,7 @@ class RunQueueData:
logger.verbose("Assign Weightings") logger.verbose("Assign Weightings")
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Generate a list of reverse dependencies to ease future calculations # Generate a list of reverse dependencies to ease future calculations
for tid in self.runtaskentries: for tid in self.runtaskentries:
@@ -1066,6 +1072,7 @@ class RunQueueData:
self.runtaskentries[dep].revdeps.add(tid) self.runtaskentries[dep].revdeps.add(tid)
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Identify tasks at the end of dependency chains # Identify tasks at the end of dependency chains
# Error on circular dependency loops (length two) # Error on circular dependency loops (length two)
@@ -1082,12 +1089,14 @@ class RunQueueData:
logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints)) logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Calculate task weights # Calculate task weights
# Check of higher length circular dependencies # Check of higher length circular dependencies
self.runq_weight = self.calculate_task_weights(endpoints) self.runq_weight = self.calculate_task_weights(endpoints)
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Sanity Check - Check for multiple tasks building the same provider # Sanity Check - Check for multiple tasks building the same provider
for mc in self.dataCaches: for mc in self.dataCaches:
@@ -1188,6 +1197,7 @@ class RunQueueData:
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
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 # Iterate over the task list looking for tasks with a 'setscene' function
self.runq_setscene_tids = set() self.runq_setscene_tids = set()
@@ -1200,6 +1210,7 @@ class RunQueueData:
self.runq_setscene_tids.add(tid) self.runq_setscene_tids.add(tid)
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
# Invalidate task if force mode active # Invalidate task if force mode active
if self.cooker.configuration.force: if self.cooker.configuration.force:
@@ -1216,6 +1227,7 @@ class RunQueueData:
invalidate_task(fn + ":" + st, True) invalidate_task(fn + ":" + st, True)
self.init_progress_reporter.next_stage() 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 # Create and print to the logs a virtual/xxxx -> PN (fn) table
for mc in taskData: for mc in taskData:
@@ -1228,6 +1240,7 @@ class RunQueueData:
bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc]) bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
self.init_progress_reporter.next_stage() self.init_progress_reporter.next_stage()
bb.event.check_for_interrupts(self.cooker.data)
bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids) bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
@@ -1240,6 +1253,7 @@ class RunQueueData:
dealtwith.add(tid) dealtwith.add(tid)
todeal.remove(tid) todeal.remove(tid)
self.prepare_task_hash(tid) self.prepare_task_hash(tid)
bb.event.check_for_interrupts(self.cooker.data)
bb.parse.siggen.writeout_file_checksum_cache() bb.parse.siggen.writeout_file_checksum_cache()
@@ -1483,6 +1497,7 @@ class RunQueue:
""" """
retval = True retval = True
bb.event.check_for_interrupts(self.cooker.data)
if self.state is runQueuePrepare: if self.state is runQueuePrepare:
# NOTE: if you add, remove or significantly refactor the stages of this # NOTE: if you add, remove or significantly refactor the stages of this