support exceptions raised by RPCs

This commit is contained in:
Sebastien Bourdeauducq 2014-12-20 21:33:22 +08:00
parent 8ea21f544d
commit f7232fd3d1
6 changed files with 103 additions and 19 deletions

View File

@ -10,6 +10,7 @@ from artiq.language import units
from artiq.language.context import * from artiq.language.context import *
from artiq.coredevice.runtime import Environment from artiq.coredevice.runtime import Environment
from artiq.coredevice import runtime_exceptions from artiq.coredevice import runtime_exceptions
from artiq.coredevice.rpc_wrapper import RPCWrapper
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -69,6 +70,7 @@ class Comm(AutoContext):
self.port.flush() self.port.flush()
self.set_remote_baud(self.baud_rate) self.set_remote_baud(self.baud_rate)
self.set_baud(self.baud_rate) self.set_baud(self.baud_rate)
self.rpc_wrapper = RPCWrapper()
def set_baud(self, baud): def set_baud(self, baud):
self.port.baudrate = baud self.port.baudrate = baud
@ -183,19 +185,18 @@ class Comm(AutoContext):
if type_tag == "l": if type_tag == "l":
r.append(self._receive_rpc_values()) r.append(self._receive_rpc_values())
def _serve_rpc(self, rpc_map): def _serve_rpc(self, rpc_map, user_exception_map):
(rpc_num, ) = struct.unpack(">h", _read_exactly(self.port, 2)) rpc_num = struct.unpack(">h", _read_exactly(self.port, 2))[0]
args = self._receive_rpc_values() args = self._receive_rpc_values()
logger.debug("rpc service: {} ({})".format(rpc_num, args)) logger.debug("rpc service: {} ({})".format(rpc_num, args))
r = rpc_map[rpc_num](*args) eid, r = self.rpc_wrapper.run_rpc(user_exception_map, rpc_map[rpc_num], args)
if r is None: _write_exactly(self.port, struct.pack(">ll", eid, r))
r = 0
_write_exactly(self.port, struct.pack(">l", r))
logger.debug("rpc service: {} ({}) == {}".format( logger.debug("rpc service: {} ({}) == {}".format(
rpc_num, args, r)) rpc_num, args, r))
def _serve_exception(self, user_exception_map): def _serve_exception(self, user_exception_map):
(eid, ) = struct.unpack(">l", _read_exactly(self.port, 4)) eid = struct.unpack(">l", _read_exactly(self.port, 4))[0]
self.rpc_wrapper.filter_rpc_exception(eid)
if eid < core_language.first_user_eid: if eid < core_language.first_user_eid:
exception = runtime_exceptions.exception_map[eid] exception = runtime_exceptions.exception_map[eid]
else: else:
@ -206,7 +207,7 @@ class Comm(AutoContext):
while True: while True:
msg = self._get_device_msg() msg = self._get_device_msg()
if msg == _D2HMsgType.RPC_REQUEST: if msg == _D2HMsgType.RPC_REQUEST:
self._serve_rpc(rpc_map) self._serve_rpc(rpc_map, user_exception_map)
elif msg == _D2HMsgType.KERNEL_EXCEPTION: elif msg == _D2HMsgType.KERNEL_EXCEPTION:
self._serve_exception(user_exception_map) self._serve_exception(user_exception_map)
elif msg == _D2HMsgType.KERNEL_FINISHED: elif msg == _D2HMsgType.KERNEL_FINISHED:

View File

@ -0,0 +1,40 @@
from artiq.coredevice.runtime_exceptions import exception_map, _RPCException
def _lookup_exception(d, e):
for eid, exception in d.items():
if isinstance(e, exception):
return eid
return 0
class RPCWrapper:
def __init__(self):
self.last_exception = None
def run_rpc(self, user_exception_map, fn, args):
eid = 0
r = None
try:
r = fn(*args)
except Exception as e:
eid = _lookup_exception(user_exception_map, e)
if not eid:
eid = _lookup_exception(exception_map, e)
if eid:
self.last_exception = None
else:
self.last_exception = e
eid = _RPCException.eid
if r is None:
r = 0
else:
r = int(r)
return eid, r
def filter_rpc_exception(self, eid):
if eid == _RPCException.eid:
raise self.last_exception

View File

@ -9,7 +9,11 @@ class OutOfMemory(RuntimeException):
"""Raised when the runtime fails to allocate memory. """Raised when the runtime fails to allocate memory.
""" """
eid = 0 eid = 1
class _RPCException(RuntimeException):
eid = 2
class RTIOUnderflow(RuntimeException): class RTIOUnderflow(RuntimeException):
@ -19,11 +23,9 @@ class RTIOUnderflow(RuntimeException):
The offending event is discarded and the RTIO core keeps operating. The offending event is discarded and the RTIO core keeps operating.
""" """
eid = 1 eid = 3
# Raised by RTIO driver for regular RTIO.
# Raised by runtime for DDS FUD.
class RTIOSequenceError(RuntimeException): class RTIOSequenceError(RuntimeException):
"""Raised when an event is submitted on a given channel with a timestamp """Raised when an event is submitted on a given channel with a timestamp
not larger than the previous one. not larger than the previous one.
@ -31,7 +33,7 @@ class RTIOSequenceError(RuntimeException):
The offending event is discarded and the RTIO core keeps operating. The offending event is discarded and the RTIO core keeps operating.
""" """
eid = 2 eid = 4
class RTIOOverflow(RuntimeException): class RTIOOverflow(RuntimeException):
@ -43,7 +45,7 @@ class RTIOOverflow(RuntimeException):
the exception is caught, and events will be partially retrieved. the exception is caught, and events will be partially retrieved.
""" """
eid = 3 eid = 5
exception_map = {e.eid: e for e in globals().values() exception_map = {e.eid: e for e in globals().values()

View File

@ -170,6 +170,25 @@ class _Exceptions(AutoContext):
self.trace.append(104) self.trace.append(104)
class _RPCExceptions(AutoContext):
def build(self):
self.success = False
def exception_raiser(self):
raise _MyException
@kernel
def do_not_catch(self):
self.exception_raiser()
@kernel
def catch(self):
try:
self.exception_raiser()
except _MyException:
self.success = True
@unittest.skipIf(no_hardware, "no hardware") @unittest.skipIf(no_hardware, "no hardware")
class ExecutionCase(unittest.TestCase): class ExecutionCase(unittest.TestCase):
def test_primes(self): def test_primes(self):
@ -219,6 +238,17 @@ class ExecutionCase(unittest.TestCase):
_run_on_host(_Exceptions, trace=t_host) _run_on_host(_Exceptions, trace=t_host)
self.assertEqual(t_device, t_host) self.assertEqual(t_device, t_host)
def test_rpc_exceptions(self):
comm = comm_serial.Comm()
try:
uut = _RPCExceptions(core=core.Core(comm=comm))
with self.assertRaises(_MyException):
uut.do_not_catch()
uut.catch()
self.assertTrue(uut.success)
finally:
comm.close()
class _RTIOLoopback(AutoContext): class _RTIOLoopback(AutoContext):
i = Device("ttl_in") i = Device("ttl_in")

View File

@ -4,6 +4,7 @@
#include <generated/csr.h> #include <generated/csr.h>
#include "comm.h" #include "comm.h"
#include "exceptions.h"
/* host to device */ /* host to device */
enum { enum {
@ -224,6 +225,8 @@ static int send_value(int type_tag, void *value)
int comm_rpc(int rpc_num, ...) int comm_rpc(int rpc_num, ...)
{ {
int type_tag; int type_tag;
int eid;
int retval;
send_char(MSGTYPE_RPC_REQUEST); send_char(MSGTYPE_RPC_REQUEST);
send_sint(rpc_num); send_sint(rpc_num);
@ -235,7 +238,13 @@ int comm_rpc(int rpc_num, ...)
va_end(args); va_end(args);
send_char(0); send_char(0);
return receive_int(); eid = receive_int();
retval = receive_int();
if(eid != EID_NONE)
exception_raise(eid);
return retval;
} }
void comm_log(const char *fmt, ...) void comm_log(const char *fmt, ...)

View File

@ -2,10 +2,12 @@
#define __EXCEPTIONS_H #define __EXCEPTIONS_H
enum { enum {
EID_OUT_OF_MEMORY = 0, EID_NONE = 0,
EID_RTIO_UNDERFLOW = 1, EID_OUT_OF_MEMORY = 1,
EID_RTIO_SEQUENCE_ERROR = 2, EID_RPC_EXCEPTION = 2,
EID_RTIO_OVERFLOW = 3, EID_RTIO_UNDERFLOW = 3,
EID_RTIO_SEQUENCE_ERROR = 4,
EID_RTIO_OVERFLOW = 5,
}; };
int exception_setjmp(void *jb) __attribute__((returns_twice)); int exception_setjmp(void *jb) __attribute__((returns_twice));