feat: speed up run time constructed method handlers (#275)

This commit is contained in:
J. Nick Koston
2023-12-03 20:28:04 -10:00
committed by GitHub
parent 7ede6bb438
commit 9f54fc3194
4 changed files with 119 additions and 72 deletions

View File

@@ -57,3 +57,11 @@ cdef class BaseMessageBus:
cpdef _call(self, Message msg, object callback) cpdef _call(self, Message msg, object callback)
cpdef next_serial(self) cpdef next_serial(self)
cpdef void _callback_method_handler(
self,
ServiceInterface interface,
_Method method,
Message msg,
object send_reply
)

View File

@@ -865,67 +865,64 @@ class BaseMessageBus:
return_handler(msg, None) return_handler(msg, None)
del self._method_return_handlers[msg.reply_serial] del self._method_return_handlers[msg.reply_serial]
def _callback_method_handler(
self,
interface: ServiceInterface,
method: _Method,
msg: Message,
send_reply: Callable[[Message], None],
) -> None:
"""This is the callback that will be called when a method call is."""
args = ServiceInterface._c_msg_body_to_args(msg) if msg.unix_fds else msg.body
result = method.fn(interface, *args)
if send_reply is BLOCK_UNEXPECTED_REPLY or _expects_reply(msg) is False:
return
body, fds = ServiceInterface._c_fn_result_to_body(
result,
signature_tree=method.out_signature_tree,
replace_fds=self._negotiate_unix_fd,
)
send_reply(
Message(
message_type=MessageType.METHOD_RETURN,
reply_serial=msg.serial,
destination=msg.sender,
signature=method.out_signature,
body=body,
unix_fds=fds,
)
)
def _make_method_handler( def _make_method_handler(
self, interface: ServiceInterface, method: _Method self, interface: ServiceInterface, method: _Method
) -> Callable[[Message, Callable[[Message], None]], None]: ) -> Callable[[Message, Callable[[Message], None]], None]:
method_fn = method.fn return partial(self._callback_method_handler, interface, method)
out_signature_tree = method.out_signature_tree
negotiate_unix_fd = self._negotiate_unix_fd
out_signature = method.out_signature
message_type_method_return = MessageType.METHOD_RETURN
msg_body_to_args = ServiceInterface._msg_body_to_args
fn_result_to_body = ServiceInterface._fn_result_to_body
def _callback_method_handler(
msg: Message, send_reply: Callable[[Message], None]
) -> None:
"""This is the callback that will be called when a method call is."""
args = msg_body_to_args(msg) if msg.unix_fds else msg.body
result = method_fn(interface, *args)
if send_reply is BLOCK_UNEXPECTED_REPLY or _expects_reply(msg) is False:
return
body, fds = fn_result_to_body(
result,
signature_tree=out_signature_tree,
replace_fds=negotiate_unix_fd,
)
send_reply(
Message(
message_type=message_type_method_return,
reply_serial=msg.serial,
destination=msg.sender,
signature=out_signature,
body=body,
unix_fds=fds,
)
)
return _callback_method_handler
def _find_message_handler( def _find_message_handler(
self, msg: _Message self, msg: _Message
) -> Optional[Callable[[Message, Callable[[Message], None]], None]]: ) -> Optional[Callable[[Message, Callable[[Message], None]], None]]:
if ( if msg.interface.startswith("org.freedesktop.DBus."):
msg.interface == "org.freedesktop.DBus.Introspectable" if (
and msg.member == "Introspect" msg.interface == "org.freedesktop.DBus.Introspectable"
and msg.signature == "" and msg.member == "Introspect"
): and msg.signature == ""
return self._default_introspect_handler ):
return self._default_introspect_handler
if msg.interface == "org.freedesktop.DBus.Properties": if msg.interface == "org.freedesktop.DBus.Properties":
return self._default_properties_handler return self._default_properties_handler
if msg.interface == "org.freedesktop.DBus.Peer": if msg.interface == "org.freedesktop.DBus.Peer":
if msg.member == "Ping" and msg.signature == "": if msg.member == "Ping" and msg.signature == "":
return self._default_ping_handler return self._default_ping_handler
elif msg.member == "GetMachineId" and msg.signature == "": elif msg.member == "GetMachineId" and msg.signature == "":
return self._default_get_machine_id_handler return self._default_get_machine_id_handler
if ( if (
msg.interface == "org.freedesktop.DBus.ObjectManager" msg.interface == "org.freedesktop.DBus.ObjectManager"
and msg.member == "GetManagedObjects" and msg.member == "GetManagedObjects"
): ):
return self._default_get_managed_objects_handler return self._default_get_managed_objects_handler
msg_path = msg.path msg_path = msg.path
if msg_path: if msg_path:

View File

@@ -2,6 +2,7 @@
import cython import cython
from .message cimport Message
from .signature cimport SignatureTree from .signature cimport SignatureTree
@@ -16,6 +17,14 @@ cdef class _Method:
cdef public SignatureTree in_signature_tree cdef public SignatureTree in_signature_tree
cdef public SignatureTree out_signature_tree cdef public SignatureTree out_signature_tree
cdef tuple _real_fn_result_to_body(
object result,
SignatureTree signature_tree,
bint replace_fds
)
cdef class ServiceInterface: cdef class ServiceInterface:
cdef public str name cdef public str name
@@ -30,3 +39,13 @@ cdef class ServiceInterface:
@staticmethod @staticmethod
cdef object _c_get_handler(ServiceInterface interface, _Method method, object bus) cdef object _c_get_handler(ServiceInterface interface, _Method method, object bus)
@staticmethod
cdef list _c_msg_body_to_args(Message msg)
@staticmethod
cdef tuple _c_fn_result_to_body(
object result,
SignatureTree signature_tree,
bint replace_fds,
)

View File

@@ -326,6 +326,35 @@ def dbus_property(
return decorator return decorator
def _real_fn_result_to_body(
result: Optional[Any],
signature_tree: SignatureTree,
replace_fds: bool,
) -> Tuple[List[Any], List[int]]:
out_len = len(signature_tree.types)
if result is None:
final_result = []
else:
if out_len == 1:
final_result = [result]
else:
result_type = type(result)
if result_type is not list and result_type is not tuple:
raise SignatureBodyMismatchError(
"Expected signal to return a list or tuple of arguments"
)
final_result = result
if out_len != len(final_result):
raise SignatureBodyMismatchError(
f"Signature and function return mismatch, expected {len(signature_tree.types)} arguments but got {len(result)}"
)
if not replace_fds:
return final_result, []
return replace_fds_with_idx(signature_tree, final_result)
class ServiceInterface: class ServiceInterface:
"""An abstract class that can be extended by the user to define DBus services. """An abstract class that can be extended by the user to define DBus services.
@@ -505,6 +534,11 @@ class ServiceInterface:
@staticmethod @staticmethod
def _msg_body_to_args(msg: Message) -> List[Any]: def _msg_body_to_args(msg: Message) -> List[Any]:
return ServiceInterface._c_msg_body_to_args(msg)
@staticmethod
def _c_msg_body_to_args(msg: Message) -> List[Any]:
# https://github.com/cython/cython/issues/3327
if not signature_contains_type(msg.signature_tree, msg.body, "h"): if not signature_contains_type(msg.signature_tree, msg.body, "h"):
return msg.body return msg.body
@@ -520,31 +554,20 @@ class ServiceInterface:
result: Optional[Any], result: Optional[Any],
signature_tree: SignatureTree, signature_tree: SignatureTree,
replace_fds: bool = True, replace_fds: bool = True,
) -> Tuple[List[Any], List[int]]:
return _real_fn_result_to_body(result, signature_tree, replace_fds)
@staticmethod
def _c_fn_result_to_body(
result: Optional[Any],
signature_tree: SignatureTree,
replace_fds: bool,
) -> Tuple[List[Any], List[int]]: ) -> Tuple[List[Any], List[int]]:
"""The high level interfaces may return single values which may be """The high level interfaces may return single values which may be
wrapped in a list to be a message body. Also they may return fds wrapped in a list to be a message body. Also they may return fds
directly for type 'h' which need to be put into an external list.""" directly for type 'h' which need to be put into an external list."""
out_len = len(signature_tree.types) # https://github.com/cython/cython/issues/3327
if result is None: return _real_fn_result_to_body(result, signature_tree, replace_fds)
result = []
else:
if out_len == 1:
result = [result]
else:
result_type = type(result)
if result_type is not list and result_type is not tuple:
raise SignatureBodyMismatchError(
"Expected signal to return a list or tuple of arguments"
)
if out_len != len(result):
raise SignatureBodyMismatchError(
f"Signature and function return mismatch, expected {len(signature_tree.types)} arguments but got {len(result)}"
)
if not replace_fds:
return result, []
return replace_fds_with_idx(signature_tree, result)
@staticmethod @staticmethod
def _handle_signal( def _handle_signal(