mirror of
https://git.yoctoproject.org/poky
synced 2026-05-02 18:32:15 +02:00
bitbake: process/cooker/command: Fix currentAsyncCommand locking/races
currentAsyncCommand currently doesn't have any locking and we have a conflict in "idle" conditions since the idle functions count needs to be zero *and* there needs to be no active command. Move the changes/checks of currentAsyncCommand to within the lock and then we can add it to the condition for idle, simplifying some of the code. (Bitbake rev: b5215887d2f8ea3f28f1ebda721bd5b8f93ec7f3) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
@@ -51,13 +51,14 @@ class Command:
|
||||
"""
|
||||
A queue of asynchronous commands for bitbake
|
||||
"""
|
||||
def __init__(self, cooker):
|
||||
def __init__(self, cooker, process_server):
|
||||
self.cooker = cooker
|
||||
self.cmds_sync = CommandsSync()
|
||||
self.cmds_async = CommandsAsync()
|
||||
self.remotedatastores = None
|
||||
|
||||
# FIXME Add lock for this
|
||||
self.process_server = process_server
|
||||
# Access with locking using process_server.{get/set/clear}_async_cmd()
|
||||
self.currentAsyncCommand = None
|
||||
|
||||
def runCommand(self, commandline, process_server, ro_only=False):
|
||||
@@ -99,18 +100,14 @@ class Command:
|
||||
return None, traceback.format_exc()
|
||||
else:
|
||||
return result, None
|
||||
if self.currentAsyncCommand is not None:
|
||||
# Wait for the idle loop to have cleared (30s max)
|
||||
process_server.wait_for_idle(timeout=30)
|
||||
if self.currentAsyncCommand is not None:
|
||||
return None, "Busy (%s in progress)" % self.currentAsyncCommand[0]
|
||||
if command not in CommandsAsync.__dict__:
|
||||
return None, "No such command"
|
||||
self.currentAsyncCommand = (command, commandline)
|
||||
self.cooker.idleCallBackRegister(self.runAsyncCommand, None)
|
||||
if not process_server.set_async_cmd((command, commandline)):
|
||||
return None, "Busy (%s in progress)" % self.process_server.get_async_cmd()[0]
|
||||
self.cooker.idleCallBackRegister(self.runAsyncCommand, process_server)
|
||||
return True, None
|
||||
|
||||
def runAsyncCommand(self, _, _2, halt):
|
||||
def runAsyncCommand(self, _, process_server, halt):
|
||||
try:
|
||||
self.cooker.process_inotify_updates_apply()
|
||||
if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
|
||||
@@ -118,8 +115,9 @@ class Command:
|
||||
# and then raise BBHandledException triggering an exit
|
||||
self.cooker.updateCache()
|
||||
return bb.server.process.idleFinish("Cooker in error state")
|
||||
if self.currentAsyncCommand is not None:
|
||||
(command, options) = self.currentAsyncCommand
|
||||
cmd = process_server.get_async_cmd()
|
||||
if cmd is not None:
|
||||
(command, options) = cmd
|
||||
commandmethod = getattr(CommandsAsync, command)
|
||||
needcache = getattr( commandmethod, "needcache" )
|
||||
if needcache and self.cooker.state != bb.cooker.state.running:
|
||||
@@ -153,7 +151,7 @@ class Command:
|
||||
else:
|
||||
bb.event.fire(CommandCompleted(), self.cooker.data)
|
||||
self.cooker.finishcommand()
|
||||
self.currentAsyncCommand = None
|
||||
self.process_server.clear_async_cmd()
|
||||
|
||||
def reset(self):
|
||||
if self.remotedatastores:
|
||||
@@ -746,7 +744,7 @@ class CommandsAsync:
|
||||
"""
|
||||
event = params[0]
|
||||
bb.event.fire(eval(event), command.cooker.data)
|
||||
command.currentAsyncCommand = None
|
||||
process_server.clear_async_cmd()
|
||||
triggerEvent.needcache = False
|
||||
|
||||
def resetCooker(self, command, params):
|
||||
|
||||
@@ -149,7 +149,7 @@ class BBCooker:
|
||||
Manages one bitbake build run
|
||||
"""
|
||||
|
||||
def __init__(self, featureSet=None, idleCallBackRegister=None, waitIdle=None):
|
||||
def __init__(self, featureSet=None, server=None):
|
||||
self.recipecaches = None
|
||||
self.eventlog = None
|
||||
self.skiplist = {}
|
||||
@@ -163,8 +163,12 @@ class BBCooker:
|
||||
|
||||
self.configuration = bb.cookerdata.CookerConfiguration()
|
||||
|
||||
self.idleCallBackRegister = idleCallBackRegister
|
||||
self.waitIdle = waitIdle
|
||||
self.process_server = server
|
||||
self.idleCallBackRegister = None
|
||||
self.waitIdle = None
|
||||
if server:
|
||||
self.idleCallBackRegister = server.register_idle_function
|
||||
self.waitIdle = server.wait_for_idle
|
||||
|
||||
bb.debug(1, "BBCooker starting %s" % time.time())
|
||||
sys.stdout.flush()
|
||||
@@ -203,7 +207,7 @@ class BBCooker:
|
||||
except UnsupportedOperation:
|
||||
pass
|
||||
|
||||
self.command = bb.command.Command(self)
|
||||
self.command = bb.command.Command(self, self.process_server)
|
||||
self.state = state.initial
|
||||
|
||||
self.parser = None
|
||||
|
||||
@@ -154,10 +154,30 @@ class ProcessServer():
|
||||
|
||||
return ret
|
||||
|
||||
def _idle_check(self):
|
||||
return len(self._idlefuns) == 0 and self.cooker.command.currentAsyncCommand is None
|
||||
|
||||
def wait_for_idle(self, timeout=30):
|
||||
# Wait for the idle loop to have cleared
|
||||
with self.idle_cond:
|
||||
self.idle_cond.wait_for(lambda: len(self._idlefuns) == 0, timeout)
|
||||
with bb.utils.lock_timeout(self._idlefuncsLock):
|
||||
return self.idle_cond.wait_for(self._idle_check, timeout) is not False
|
||||
|
||||
def set_async_cmd(self, cmd):
|
||||
with bb.utils.lock_timeout(self._idlefuncsLock):
|
||||
ret = self.idle_cond.wait_for(self._idle_check, 30)
|
||||
if ret is False:
|
||||
return False
|
||||
self.cooker.command.currentAsyncCommand = cmd
|
||||
return True
|
||||
|
||||
def clear_async_cmd(self):
|
||||
with bb.utils.lock_timeout(self._idlefuncsLock):
|
||||
self.cooker.command.currentAsyncCommand = None
|
||||
self.idle_cond.notify_all()
|
||||
|
||||
def get_async_cmd(self):
|
||||
with bb.utils.lock_timeout(self._idlefuncsLock):
|
||||
return self.cooker.command.currentAsyncCommand
|
||||
|
||||
def main(self):
|
||||
self.cooker.pre_serve()
|
||||
@@ -183,11 +203,9 @@ class ProcessServer():
|
||||
self.controllersock = False
|
||||
if self.haveui:
|
||||
# Wait for the idle loop to have cleared (30s max)
|
||||
self.wait_for_idle(30)
|
||||
if self.cooker.command.currentAsyncCommand is not None:
|
||||
if not self.wait_for_idle(30):
|
||||
serverlog("Idle loop didn't finish queued commands after 30s, exiting.")
|
||||
self.quit = True
|
||||
|
||||
fds.remove(self.command_channel)
|
||||
bb.event.unregister_UIHhandler(self.event_handle, True)
|
||||
self.command_channel_reply.writer.close()
|
||||
@@ -624,7 +642,7 @@ def execServer(lockfd, readypipeinfd, lockname, sockname, server_timeout, xmlrpc
|
||||
writer = ConnectionWriter(readypipeinfd)
|
||||
try:
|
||||
featureset = []
|
||||
cooker = bb.cooker.BBCooker(featureset, server.register_idle_function, server.wait_for_idle)
|
||||
cooker = bb.cooker.BBCooker(featureset, server)
|
||||
cooker.configuration.profile = profile
|
||||
except bb.BBHandledException:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user