mirror of https://github.com/m-labs/artiq.git
support exceptions raised by RPCs
This commit is contained in:
parent
8ea21f544d
commit
f7232fd3d1
|
@ -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:
|
||||||
|
|
|
@ -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
|
|
@ -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()
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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, ...)
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Loading…
Reference in New Issue