self.running = False
self.finished = False
self.terminated = False
+ self.exited = False
self.checkretcode = False
self.retcodes = [0]
self.task = task
self.logstderr = logstderr
self.silent = silent
- self.task.procs_pending.append(self)
+ self.task.update_proc_pending(self)
for dep in self.deps:
dep.register_dep(self)
self.exit_callback = None
self.error_callback = None
+ if self.task.timeout_reached:
+ self.terminate(True)
+
def register_dep(self, next_proc):
if self.finished:
next_proc.poll()
except PermissionError:
pass
self.p.terminate()
- self.task.procs_running.remove(self)
- all_procs_running.remove(self)
+ self.task.update_proc_stopped(self)
+ elif not self.finished and not self.terminated and not self.exited:
+ self.task.update_proc_canceled(self)
self.terminated = True
- def poll(self):
- if self.finished or self.terminated:
+ def poll(self, force_unchecked=False):
+ if self.task.task_local_abort and not force_unchecked:
+ try:
+ self.poll(True)
+ except SbyAbort:
+ self.task.terminate(True)
+ return
+ if self.finished or self.terminated or self.exited:
return
if not self.running:
self.p = subprocess.Popen(self.cmdline, shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
stderr=(subprocess.STDOUT if self.logstderr else None))
- self.task.procs_pending.remove(self)
- self.task.procs_running.append(self)
- all_procs_running.append(self)
+ self.task.update_proc_running(self)
self.running = True
return
if self.p.poll() is not None:
if not self.silent:
self.task.log(f"{self.info}: finished (returncode={self.p.returncode})")
- self.task.procs_running.remove(self)
- all_procs_running.remove(self)
+ self.task.update_proc_stopped(self)
self.running = False
+ self.exited = True
if self.p.returncode == 127:
- self.task.status = "ERROR"
if not self.silent:
self.task.log(f"{self.info}: COMMAND NOT FOUND. ERROR.")
self.handle_error(self.p.returncode)
self.terminated = True
- self.task.terminate()
+ self.task.proc_failed(self)
return
if self.checkretcode and self.p.returncode not in self.retcodes:
- self.task.status = "ERROR"
if not self.silent:
self.task.log(f"{self.info}: task failed. ERROR.")
self.handle_error(self.p.returncode)
self.terminated = True
- self.task.terminate()
+ self.task.proc_failed(self)
return
self.handle_exit(self.p.returncode)
def error(self, logmessage):
raise SbyAbort(logmessage)
+
+class SbyTaskloop:
+ def __init__(self):
+ self.procs_pending = []
+ self.procs_running = []
+ self.tasks = []
+ self.poll_now = False
+
+ def run(self):
+ for proc in self.procs_pending:
+ proc.poll()
+
+ while len(self.procs_running) or self.poll_now:
+ fds = []
+ for proc in self.procs_running:
+ if proc.running:
+ fds.append(proc.p.stdout)
+
+ if not self.poll_now:
+ if os.name == "posix":
+ try:
+ select(fds, [], [], 1.0) == ([], [], [])
+ except InterruptedError:
+ pass
+ else:
+ sleep(0.1)
+ self.poll_now = False
+
+ for proc in self.procs_running:
+ proc.poll()
+
+ for proc in self.procs_pending:
+ proc.poll()
+
+ tasks = self.tasks
+ self.tasks = []
+ for task in tasks:
+ task.check_timeout()
+ if task.procs_pending or task.procs_running:
+ self.tasks.append(task)
+ else:
+ task.exit_callback()
+
+ for task in self.tasks:
+ task.exit_callback()
+
+
class SbyTask(SbyConfig):
- def __init__(self, sbyconfig, workdir, early_logs, reusedir):
+ def __init__(self, sbyconfig, workdir, early_logs, reusedir, taskloop=None):
super().__init__()
self.used_options = set()
self.models = dict()
self.expect = list()
self.design_hierarchy = None
self.precise_prop_status = False
+ self.timeout_reached = False
+ self.task_local_abort = False
yosys_program_prefix = "" ##yosys-program-prefix##
self.exe_paths = {
"pono": os.getenv("PONO", "pono"),
}
+ self.taskloop = taskloop or SbyTaskloop()
+ self.taskloop.tasks.append(self)
+
self.procs_running = []
self.procs_pending = []
for line in sbyconfig:
print(line, file=f)
- def taskloop(self):
- for proc in self.procs_pending:
- proc.poll()
+ def check_timeout(self):
+ if self.opt_timeout is not None:
+ total_clock_time = int(monotonic() - self.start_clock_time)
+ if total_clock_time > self.opt_timeout:
+ self.log(f"Reached TIMEOUT ({self.opt_timeout} seconds). Terminating all subprocesses.")
+ self.status = "TIMEOUT"
+ self.terminate(timeout=True)
- while len(self.procs_running):
- fds = []
- for proc in self.procs_running:
- if proc.running:
- fds.append(proc.p.stdout)
+ def update_proc_pending(self, proc):
+ self.procs_pending.append(proc)
+ self.taskloop.procs_pending.append(proc)
- if os.name == "posix":
- try:
- select(fds, [], [], 1.0) == ([], [], [])
- except InterruptedError:
- pass
- else:
- sleep(0.1)
+ def update_proc_running(self, proc):
+ self.procs_pending.remove(proc)
+ self.taskloop.procs_pending.remove(proc)
- for proc in self.procs_running:
- proc.poll()
+ self.procs_running.append(proc)
+ self.taskloop.procs_running.append(proc)
+ all_procs_running.append(proc)
- for proc in self.procs_pending:
- proc.poll()
+ def update_proc_stopped(self, proc):
+ self.procs_running.remove(proc)
+ self.taskloop.procs_running.remove(proc)
+ all_procs_running.remove(proc)
- if self.opt_timeout is not None:
- total_clock_time = int(monotonic() - self.start_clock_time)
- if total_clock_time > self.opt_timeout:
- self.log(f"Reached TIMEOUT ({self.opt_timeout} seconds). Terminating all subprocesses.")
- self.status = "TIMEOUT"
- self.terminate(timeout=True)
+ def update_proc_canceled(self, proc):
+ self.procs_pending.remove(proc)
+ self.taskloop.procs_pending.remove(proc)
def log(self, logmessage):
tm = localtime()
return self.models[model_name]
def terminate(self, timeout=False):
+ if timeout:
+ self.timeout_reached = True
for proc in list(self.procs_running):
proc.terminate(timeout=timeout)
+ for proc in list(self.procs_pending):
+ proc.terminate(timeout=timeout)
+
+ def proc_failed(self, proc):
+ # proc parameter used by autotune override
+ self.status = "ERROR"
+ self.terminate()
def update_status(self, new_status):
assert new_status in ["PASS", "FAIL", "UNKNOWN", "ERROR"]
assert 0
def run(self, setupmode):
+ self.setup_procs(setupmode)
+ if not setupmode:
+ self.taskloop.run()
+
+ def setup_procs(self, setupmode):
with open(f"{self.workdir}/config.sby", "r") as f:
self.parse_config(f)
if opt not in self.used_options:
self.error(f"Unused option: {opt}")
- self.taskloop()
-
+ def summarize(self):
total_clock_time = int(monotonic() - self.start_clock_time)
if os.name == "posix":
for line in self.summary:
print(line, file=f)
+ def exit_callback(self):
+ self.summarize()
+
def print_junit_result(self, f, junit_ts_name, junit_tc_name, junit_format_strict=False):
junit_time = strftime('%Y-%m-%dT%H:%M:%S')
if self.precise_prop_status: