2014-10-05 16:25:31 +08:00
|
|
|
import sys
|
|
|
|
import asyncio
|
|
|
|
import subprocess
|
|
|
|
import signal
|
2015-01-13 17:27:36 +08:00
|
|
|
import traceback
|
2014-10-25 16:31:34 +08:00
|
|
|
|
2015-01-17 19:38:20 +08:00
|
|
|
from artiq.protocols import pyon
|
2014-10-05 16:25:31 +08:00
|
|
|
|
|
|
|
|
2015-03-09 23:22:41 +08:00
|
|
|
class WorkerError(Exception):
|
2014-12-31 17:41:22 +08:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2014-10-05 16:25:31 +08:00
|
|
|
class Worker:
|
2015-02-20 03:09:11 +08:00
|
|
|
def __init__(self,
|
2014-12-08 19:22:02 +08:00
|
|
|
send_timeout=0.5, start_reply_timeout=1.0, term_timeout=1.0):
|
2015-02-20 03:09:11 +08:00
|
|
|
self.handlers = dict()
|
2014-10-05 16:25:31 +08:00
|
|
|
self.send_timeout = send_timeout
|
|
|
|
self.start_reply_timeout = start_reply_timeout
|
|
|
|
self.term_timeout = term_timeout
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
2015-03-09 23:22:41 +08:00
|
|
|
def _create_process(self):
|
2014-10-05 16:25:31 +08:00
|
|
|
self.process = yield from asyncio.create_subprocess_exec(
|
2015-01-14 12:16:49 +08:00
|
|
|
sys.executable, "-m", "artiq.master.worker_impl",
|
2014-10-05 16:25:31 +08:00
|
|
|
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
|
|
|
|
|
2015-03-09 23:22:41 +08:00
|
|
|
@asyncio.coroutine
|
|
|
|
def _end_process(self):
|
|
|
|
if self.process.returncode is not None:
|
|
|
|
return
|
|
|
|
self.process.send_signal(signal.SIGTERM)
|
|
|
|
try:
|
|
|
|
yield from asyncio.wait_for(
|
|
|
|
self.process.wait(), timeout=self.term_timeout)
|
|
|
|
except asyncio.TimeoutError:
|
|
|
|
self.process.send_signal(signal.SIGKILL)
|
|
|
|
|
2014-10-05 16:25:31 +08:00
|
|
|
@asyncio.coroutine
|
|
|
|
def _send(self, obj, timeout):
|
2014-10-25 16:31:34 +08:00
|
|
|
line = pyon.encode(obj)
|
2014-10-05 16:25:31 +08:00
|
|
|
self.process.stdin.write(line.encode())
|
|
|
|
self.process.stdin.write("\n".encode())
|
|
|
|
try:
|
|
|
|
fut = self.process.stdin.drain()
|
2014-10-23 18:48:03 +08:00
|
|
|
if fut is not (): # FIXME: why does Python return this?
|
2014-10-05 16:25:31 +08:00
|
|
|
yield from asyncio.wait_for(fut, timeout=timeout)
|
|
|
|
except asyncio.TimeoutError:
|
2015-03-09 23:22:41 +08:00
|
|
|
raise WorkerError("Timeout sending data from worker")
|
2014-10-05 16:25:31 +08:00
|
|
|
except:
|
2015-03-09 23:22:41 +08:00
|
|
|
raise WorkerError("Failed to send data to worker")
|
2014-10-05 16:25:31 +08:00
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def _recv(self, timeout):
|
|
|
|
try:
|
|
|
|
line = yield from asyncio.wait_for(
|
|
|
|
self.process.stdout.readline(), timeout=timeout)
|
|
|
|
except asyncio.TimeoutError:
|
2015-03-09 23:22:41 +08:00
|
|
|
raise WorkerError("Timeout receiving data from worker")
|
2014-10-05 16:25:31 +08:00
|
|
|
if not line:
|
2015-03-09 23:22:41 +08:00
|
|
|
return None
|
2014-10-05 16:25:31 +08:00
|
|
|
try:
|
2014-10-25 16:31:34 +08:00
|
|
|
obj = pyon.decode(line.decode())
|
2014-10-05 16:25:31 +08:00
|
|
|
except:
|
2015-03-09 23:22:41 +08:00
|
|
|
raise WorkerError("Worker sent invalid PYON data")
|
2014-10-05 16:25:31 +08:00
|
|
|
return obj
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
2015-02-21 05:11:55 +08:00
|
|
|
def run(self, rid, run_params):
|
2015-03-09 23:22:41 +08:00
|
|
|
yield from self._create_process()
|
|
|
|
|
|
|
|
try:
|
|
|
|
obj = {"rid": rid, "run_params": run_params}
|
|
|
|
yield from self._send(obj, self.send_timeout)
|
|
|
|
obj = yield from self._recv(self.start_reply_timeout)
|
|
|
|
if obj != "ack":
|
|
|
|
raise WorkerError("Incorrect acknowledgement")
|
|
|
|
while True:
|
|
|
|
obj = yield from self._recv(None)
|
|
|
|
if obj is None:
|
|
|
|
if self.process.returncode != 0:
|
|
|
|
raise WorkerError("Worker finished with status code {}"
|
|
|
|
.format(self.process.returncode))
|
|
|
|
break
|
|
|
|
action = obj["action"]
|
2014-12-31 17:41:22 +08:00
|
|
|
del obj["action"]
|
|
|
|
try:
|
|
|
|
data = self.handlers[action](**obj)
|
|
|
|
reply = {"status": "ok", "data": data}
|
|
|
|
except:
|
2015-01-13 17:27:36 +08:00
|
|
|
reply = {"status": "failed",
|
|
|
|
"message": traceback.format_exc()}
|
2014-12-31 17:41:22 +08:00
|
|
|
yield from self._send(reply, self.send_timeout)
|
2015-03-09 23:22:41 +08:00
|
|
|
finally:
|
|
|
|
yield from self._end_process()
|