forked from M-Labs/artiq
worker: split build stage from prepare
This commit is contained in:
parent
34aacd3c5f
commit
96a5d73c81
|
@ -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
|
||||||
|
|
|
@ -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, "
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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"})
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue