forked from M-Labs/artiq
1
0
Fork 0

worker: split build stage from prepare

This commit is contained in:
Sebastien Bourdeauducq 2015-07-09 13:18:12 +02:00
parent 34aacd3c5f
commit 96a5d73c81
5 changed files with 40 additions and 18 deletions

View File

@ -1,6 +1,6 @@
from inspect import isclass from inspect import isclass
__all__ = ["Experiment", "has_analyze", "is_experiment"] __all__ = ["Experiment", "is_experiment"]
class Experiment: class Experiment:
@ -9,11 +9,28 @@ class Experiment:
Deriving from this class enables automatic experiment discovery in Deriving from this class enables automatic experiment discovery in
Python modules. Python modules.
""" """
def prepare(self):
"""Entry point for pre-computing data necessary for running the
experiment.
Doing such computations outside of ``run`` enables more efficient
scheduling of multiple experiments that need to access the shared
hardware during part of their execution.
This method must not interact with the hardware.
"""
pass
def run(self): def run(self):
"""The main entry point of the experiment. """The main entry point of the experiment.
This method must be overloaded by the user to implement the main This method must be overloaded by the user to implement the main
control flow of the experiment. control flow of the experiment.
This method may interact with the hardware.
The experiment may call the scheduler's ``pause`` method while in
``run``.
""" """
raise NotImplementedError raise NotImplementedError
@ -32,11 +49,6 @@ class Experiment:
pass pass
def has_analyze(experiment):
"""Checks if an experiment instance overloaded its ``analyze`` method."""
return experiment.analyze.__func__ is not Experiment.analyze
def is_experiment(o): def is_experiment(o):
"""Checks if a Python object is an instantiable experiment.""" """Checks if a Python object is an instantiable experiment."""
return isclass(o) and issubclass(o, Experiment) and o is not Experiment return isclass(o) and issubclass(o, Experiment) and o is not Experiment

View File

@ -99,13 +99,14 @@ class Run:
yield from self.worker.close() yield from self.worker.close()
del self._notifier[self.rid] del self._notifier[self.rid]
_prepare = _mk_worker_method("prepare") _build = _mk_worker_method("build")
@asyncio.coroutine @asyncio.coroutine
def prepare(self): def build(self):
yield from self._prepare(self.rid, self.pipeline_name, self.expid, yield from self._build(self.rid, self.pipeline_name, self.expid,
self.priority) self.priority)
prepare = _mk_worker_method("prepare")
run = _mk_worker_method("run") run = _mk_worker_method("run")
resume = _mk_worker_method("resume") resume = _mk_worker_method("resume")
analyze = _mk_worker_method("analyze") analyze = _mk_worker_method("analyze")
@ -188,6 +189,7 @@ class PrepareStage(TaskObject):
run.status = RunStatus.preparing run.status = RunStatus.preparing
self.flush_tracker.add(run.rid) self.flush_tracker.add(run.rid)
try: try:
yield from run.build()
yield from run.prepare() yield from run.prepare()
except: except:
logger.warning("got worker exception in prepare stage, " logger.warning("got worker exception in prepare stage, "

View File

@ -28,11 +28,11 @@ class WorkerError(Exception):
class Worker: class Worker:
def __init__(self, handlers, def __init__(self, handlers,
send_timeout=0.5, term_timeout=1.0, send_timeout=0.5, term_timeout=1.0,
prepare_timeout=15.0, results_timeout=15.0): build_timeout=15.0, results_timeout=15.0):
self.handlers = handlers self.handlers = handlers
self.send_timeout = send_timeout self.send_timeout = send_timeout
self.term_timeout = term_timeout self.term_timeout = term_timeout
self.prepare_timeout = prepare_timeout self.build_timeout = build_timeout
self.results_timeout = results_timeout self.results_timeout = results_timeout
self.rid = None self.rid = None
@ -142,7 +142,7 @@ class Worker:
[self.process.stdout.readline(), self.closed.wait()], [self.process.stdout.readline(), self.closed.wait()],
timeout=timeout, return_when=asyncio.FIRST_COMPLETED) timeout=timeout, return_when=asyncio.FIRST_COMPLETED)
if all(f.cancelled() for f in fs): if all(f.cancelled() for f in fs):
raise WorkerTimeout("Timeout sending data to worker") raise WorkerTimeout("Timeout receiving data from worker")
if self.closed.is_set(): if self.closed.is_set():
raise WorkerError("Data transmission to worker cancelled") raise WorkerError("Data transmission to worker cancelled")
line = fs[0].result() line = fs[0].result()
@ -209,16 +209,20 @@ class Worker:
return completed return completed
@asyncio.coroutine @asyncio.coroutine
def prepare(self, rid, pipeline_name, expid, priority): def build(self, rid, pipeline_name, expid, priority):
self.rid = rid self.rid = rid
yield from self._create_process() yield from self._create_process()
yield from self._worker_action( yield from self._worker_action(
{"action": "prepare", {"action": "build",
"rid": rid, "rid": rid,
"pipeline_name": pipeline_name, "pipeline_name": pipeline_name,
"expid": expid, "expid": expid,
"priority": priority}, "priority": priority},
self.prepare_timeout) self.build_timeout)
@asyncio.coroutine
def prepare(self):
yield from self._worker_action({"action": "prepare"})
@asyncio.coroutine @asyncio.coroutine
def run(self): def run(self):

View File

@ -117,7 +117,7 @@ def main():
while True: while True:
obj = get_object() obj = get_object()
action = obj["action"] action = obj["action"]
if action == "prepare": if action == "build":
start_time = time.localtime() start_time = time.localtime()
rid = obj["rid"] rid = obj["rid"]
pipeline_name = obj["pipeline_name"] pipeline_name = obj["pipeline_name"]
@ -131,6 +131,9 @@ def main():
**expid["arguments"]) **expid["arguments"])
rdb.build() rdb.build()
put_object({"action": "completed"}) put_object({"action": "completed"})
elif action == "prepare":
exp_inst.prepare()
put_object({"action": "completed"})
elif action == "run": elif action == "run":
exp_inst.run() exp_inst.run()
put_object({"action": "completed"}) put_object({"action": "completed"})

View File

@ -32,7 +32,8 @@ class WatchdogTimeoutInBuild(Experiment, AutoDB):
@asyncio.coroutine @asyncio.coroutine
def _call_worker(worker, expid): def _call_worker(worker, expid):
try: try:
yield from worker.prepare(0, "main", expid, 0) yield from worker.build(0, "main", expid, 0)
yield from worker.prepare()
yield from worker.run() yield from worker.run()
yield from worker.analyze() yield from worker.analyze()
finally: finally: