diff --git a/bitbake/lib/bb/event.py b/bitbake/lib/bb/event.py index 6f1cb101fc..cacbac8f56 100644 --- a/bitbake/lib/bb/event.py +++ b/bitbake/lib/bb/event.py @@ -48,6 +48,16 @@ class Event(object): def __init__(self): self.pid = worker_pid + +class HeartbeatEvent(Event): + """Triggered at regular time intervals of 10 seconds. Other events can fire much more often + (runQueueTaskStarted when there are many short tasks) or not at all for long periods + of time (again runQueueTaskStarted, when there is just one long-running task), so this + event is more suitable for doing some task-independent work occassionally.""" + def __init__(self, time): + Event.__init__(self) + self.time = time + Registered = 10 AlreadyRegistered = 14 diff --git a/bitbake/lib/bb/server/process.py b/bitbake/lib/bb/server/process.py index 982fcf71c3..1654faf92b 100644 --- a/bitbake/lib/bb/server/process.py +++ b/bitbake/lib/bb/server/process.py @@ -92,6 +92,8 @@ class ProcessServer(Process, BaseImplServer): self.event = EventAdapter(event_queue) self.featurelist = featurelist self.quit = False + self.heartbeat_seconds = 1 # default, BB_HEARTBEAT_EVENT will be checked once we have a datastore. + self.next_heartbeat = time.time() self.quitin, self.quitout = Pipe() self.event_handle = multiprocessing.Value("i") @@ -101,6 +103,14 @@ class ProcessServer(Process, BaseImplServer): self.event_queue.put(event) self.event_handle.value = bb.event.register_UIHhandler(self, True) + heartbeat_event = self.cooker.data.getVar('BB_HEARTBEAT_EVENT', True) + if heartbeat_event: + try: + self.heartbeat_seconds = float(heartbeat_event) + except: + # Throwing an exception here causes bitbake to hang. + # Just warn about the invalid setting and continue + bb.warn('Ignoring invalid BB_HEARTBEAT_EVENT=%s, must be a float specifying seconds.' % heartbeat_event) bb.cooker.server_main(self.cooker, self.main) def main(self): @@ -160,6 +170,21 @@ class ProcessServer(Process, BaseImplServer): del self._idlefuns[function] self.quit = True + # Create new heartbeat event? + now = time.time() + if now >= self.next_heartbeat: + # We might have missed heartbeats. Just trigger once in + # that case and continue after the usual delay. + self.next_heartbeat += self.heartbeat_seconds + if self.next_heartbeat <= now: + self.next_heartbeat = now + self.heartbeat_seconds + heartbeat = bb.event.HeartbeatEvent(now) + bb.event.fire(heartbeat, self.cooker.data) + if nextsleep and now + nextsleep > self.next_heartbeat: + # Shorten timeout so that we we wake up in time for + # the heartbeat. + nextsleep = self.next_heartbeat - now + if nextsleep is not None: select.select(fds,[],[],nextsleep) diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py index 948f52769d..48e1223c6b 100644 --- a/bitbake/lib/bb/ui/knotty.py +++ b/bitbake/lib/bb/ui/knotty.py @@ -647,6 +647,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): bb.event.OperationCompleted, bb.event.OperationProgress, bb.event.DiskFull, + bb.event.HeartbeatEvent, bb.build.TaskProgress)): continue diff --git a/bitbake/lib/bb/ui/toasterui.py b/bitbake/lib/bb/ui/toasterui.py index b1b3684a82..17299026ab 100644 --- a/bitbake/lib/bb/ui/toasterui.py +++ b/bitbake/lib/bb/ui/toasterui.py @@ -236,6 +236,9 @@ def main(server, eventHandler, params): # pylint: disable=protected-access # the code will look into the protected variables of the event; no easy way around this + if isinstance(event, bb.event.HeartbeatEvent): + continue + if isinstance(event, bb.event.ParseStarted): if not (build_log and build_log_file_path): build_log, build_log_file_path = _open_build_log(log_dir)