class Command(object): class CommandResult(object): def __init__(self, returncode=0, process=None, duration=0.0, terminated=False, global_terminated=False): self.returncode = returncode self.process = process self.duration = duration self.terminated = terminated self.global_terminated = global_terminated def __init__(self, args, inn, out, err): self.inn_file = inn self.out_file = out self.err_file = err self.inn = None self.out = None self.err = None self.process = None self.args = args self.command = '; '.join(args.command) self.timer = Timer() self.terminated = False self.scale = 1.0 def open_streams(self): ensure_path(self.inn_file) ensure_path(self.out_file) ensure_path(self.err_file) self.inn = PIPE if self.inn_file is None else open(self.inn_file, "rb") self.out = open(self.out_file, "wb") self.err = open(self.err_file, "wb") def close_streams(self): if self.inn_file is not None: self.inn.close() self.out.close() self.err.close() def _run(self, timeout): timeout = min([max_wait_time, timeout]) * self.scale def target(): Logger.instance().info('Running command with time limit {:1.2f} s: {} in {}'.format(timeout, self.args.command, self.args.cwd)) self.process = Popen(self.args.command, stdout=self.out, stderr=self.err, stdin=self.inn, cwd=self.args.cwd) Logger.instance().info('started PID {}'.format(self.process.pid)) self.process.wait() # process itself is not limited but there is global limit Logger.instance().info('Command finished with %d' % self.process.returncode) thread = threading.Thread(target=target) thread.start() thread.join(GlobalTimeout.time_left()) if thread.is_alive(): Logger.instance().info('Terminating process') self.terminated = True self.global_terminate = GlobalTimeout.time_left() < 0 try: self.process.terminate() except Exception as e: print(e) try: self.process.kill() except Exception as e: print(e) thread.join() def run(self, timeout=max_wait_time): """ :rtype: jobs.job_processing.Command.CommandResult """ # empty command such as interpret language compilation if not self.args.command: return Command.CommandResult() self.open_streams() self.timer.tick() self._run(timeout) self.timer.tock() self.close_streams() # save global termination GlobalTimeout.decrease(self.timer.duration) # return run result return Command.CommandResult( returncode=self.process.returncode, duration=self.timer.duration*1000, terminated=self.terminated, global_terminated=self.terminated and GlobalTimeout.invalid() )