Skip to content

Commit

Permalink
feat: speed up run time constructed method handlers (#275)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Dec 4, 2023
1 parent 7ede6bb commit 9f54fc3
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 72 deletions.
8 changes: 8 additions & 0 deletions src/dbus_fast/message_bus.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,11 @@ cdef class BaseMessageBus:
cpdef _call(self, Message msg, object callback)

cpdef next_serial(self)

cpdef void _callback_method_handler(
self,
ServiceInterface interface,
_Method method,
Message msg,
object send_reply
)
99 changes: 48 additions & 51 deletions src/dbus_fast/message_bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,67 +865,64 @@ def _process_message(self, msg: _Message) -> None:
return_handler(msg, None)
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(
self, interface: ServiceInterface, method: _Method
) -> Callable[[Message, Callable[[Message], None]], None]:
method_fn = method.fn
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
return partial(self._callback_method_handler, interface, method)

def _find_message_handler(
self, msg: _Message
) -> Optional[Callable[[Message, Callable[[Message], None]], None]]:
if (
msg.interface == "org.freedesktop.DBus.Introspectable"
and msg.member == "Introspect"
and msg.signature == ""
):
return self._default_introspect_handler
if msg.interface.startswith("org.freedesktop.DBus."):
if (
msg.interface == "org.freedesktop.DBus.Introspectable"
and msg.member == "Introspect"
and msg.signature == ""
):
return self._default_introspect_handler

if msg.interface == "org.freedesktop.DBus.Properties":
return self._default_properties_handler
if msg.interface == "org.freedesktop.DBus.Properties":
return self._default_properties_handler

if msg.interface == "org.freedesktop.DBus.Peer":
if msg.member == "Ping" and msg.signature == "":
return self._default_ping_handler
elif msg.member == "GetMachineId" and msg.signature == "":
return self._default_get_machine_id_handler
if msg.interface == "org.freedesktop.DBus.Peer":
if msg.member == "Ping" and msg.signature == "":
return self._default_ping_handler
elif msg.member == "GetMachineId" and msg.signature == "":
return self._default_get_machine_id_handler

if (
msg.interface == "org.freedesktop.DBus.ObjectManager"
and msg.member == "GetManagedObjects"
):
return self._default_get_managed_objects_handler
if (
msg.interface == "org.freedesktop.DBus.ObjectManager"
and msg.member == "GetManagedObjects"
):
return self._default_get_managed_objects_handler

msg_path = msg.path
if msg_path:
Expand Down
19 changes: 19 additions & 0 deletions src/dbus_fast/service.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import cython

from .message cimport Message
from .signature cimport SignatureTree


Expand All @@ -16,6 +17,14 @@ cdef class _Method:
cdef public SignatureTree in_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 public str name
Expand All @@ -30,3 +39,13 @@ cdef class ServiceInterface:

@staticmethod
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,
)
65 changes: 44 additions & 21 deletions src/dbus_fast/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,35 @@ def decorator(fn):
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:
"""An abstract class that can be extended by the user to define DBus services.
Expand Down Expand Up @@ -505,6 +534,11 @@ def _remove_bus(interface: "ServiceInterface", bus: "BaseMessageBus") -> None:

@staticmethod
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"):
return msg.body

Expand All @@ -520,31 +554,20 @@ def _fn_result_to_body(
result: Optional[Any],
signature_tree: SignatureTree,
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]]:
"""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
directly for type 'h' which need to be put into an external list."""
out_len = len(signature_tree.types)
if result is None:
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)
# https://github.com/cython/cython/issues/3327
return _real_fn_result_to_body(result, signature_tree, replace_fds)

@staticmethod
def _handle_signal(
Expand Down

0 comments on commit 9f54fc3

Please sign in to comment.