Skip to content

Commit 54ffe5b

Browse files
committed
Use blocking function to integrate Python methods and sd-bus
Current asyncio callbacks have an issue of being garbage collected. In order to prevent that add them to a set and use `add_done_callback` to drop refrence once task completes. Make `DbusLocalMethodAsync._dbus_reply_call` a blocking function as in the future the low level API will no longer have asyncio integration. Bug reported by @arkq.
1 parent f62c8bf commit 54ffe5b

File tree

4 files changed

+36
-4
lines changed

4 files changed

+36
-4
lines changed

src/sdbus/dbus_common_elements.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from .sd_bus_internals import is_interface_name_valid, is_member_name_valid
3131

3232
if TYPE_CHECKING:
33+
from asyncio import Task
3334
from collections.abc import Callable, Sequence
3435
from types import FunctionType
3536
from typing import Any, Optional
@@ -330,6 +331,16 @@ def __init__(self) -> None:
330331
self.activated_interfaces: list[SdBusInterface] = []
331332
self.serving_object_path: Optional[str] = None
332333
self.attached_bus: Optional[SdBus] = None
334+
self._tasks: Optional[set[Task[None]]] = None
335+
336+
@property
337+
def tasks(self) -> set[Task[None]]:
338+
tasks_set = self._tasks
339+
if tasks_set is None:
340+
tasks_set = set()
341+
self._tasks = tasks_set
342+
343+
return tasks_set
333344

334345

335346
class DbusClassMeta:

src/sdbus/dbus_proxy_async_interface_base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ def new_proxy(
452452

453453
class DbusExportHandle:
454454
def __init__(self, local_meta: DbusLocalObjectMeta):
455+
self._tasks = local_meta.tasks
455456
self._dbus_slots: list[SdBusSlot] = [
456457
i.slot
457458
for i in local_meta.activated_interfaces
@@ -481,5 +482,8 @@ async def __aexit__(
481482
self.stop()
482483

483484
def stop(self) -> None:
485+
for task in self._tasks:
486+
task.cancel("D-Bus export stopped")
487+
484488
for slot in self._dbus_slots:
485489
slot.close()

src/sdbus/dbus_proxy_async_method.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2020
from __future__ import annotations
2121

22+
from asyncio import get_running_loop
2223
from contextvars import ContextVar, copy_context
2324
from inspect import iscoroutinefunction
2425
from types import FunctionType
@@ -27,6 +28,7 @@
2728

2829
from .dbus_common_elements import (
2930
DbusBoundAsync,
31+
DbusLocalObjectMeta,
3032
DbusMemberAsync,
3133
DbusMethodCommon,
3234
DbusMethodOverride,
@@ -177,16 +179,31 @@ async def _dbus_reply_call_method(
177179

178180
return await local_method(*request_message.parse_to_tuple())
179181

180-
async def _dbus_reply_call(
182+
def _dbus_reply_call(
181183
self,
182184
request_message: SdBusMessage
183185
) -> None:
184186
local_object = self.local_object_ref()
185187
if local_object is None:
186188
raise RuntimeError("Local object no longer exists!")
189+
local_meta = local_object._dbus
190+
if not isinstance(local_meta, DbusLocalObjectMeta):
191+
raise RuntimeError("D-Bus object is a remote proxy!")
187192

188-
call_context = copy_context()
193+
loop = get_running_loop()
194+
reply_task = loop.create_task(
195+
self._dbus_reply_call_async(local_object, request_message)
196+
)
197+
tasks_set = local_meta.tasks
198+
tasks_set.add(reply_task)
199+
reply_task.add_done_callback(tasks_set.discard)
189200

201+
async def _dbus_reply_call_async(
202+
self,
203+
local_object: DbusInterfaceBaseAsync,
204+
request_message: SdBusMessage
205+
) -> None:
206+
call_context = copy_context()
190207
try:
191208
reply_data = await call_context.run(
192209
self._dbus_reply_call_method,

src/sdbus/sd_bus_internals.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from typing import TYPE_CHECKING
2424

2525
if TYPE_CHECKING:
26-
from collections.abc import Callable, Coroutine, Sequence
26+
from collections.abc import Callable, Sequence
2727
from typing import Any, Optional, Union
2828

2929
DbusBasicTypes = Union[str, int, bytes, float, Any]
@@ -63,7 +63,7 @@ def add_method(
6363
signature: str, input_args_names: Sequence[str],
6464
result_signature: str, result_args_names: Sequence[str],
6565
flags: int,
66-
callback: Callable[[SdBusMessage], Coroutine[Any, Any, None]], /
66+
callback: Callable[[SdBusMessage], None], /
6767
) -> None:
6868
raise NotImplementedError(__STUB_ERROR)
6969

0 commit comments

Comments
 (0)