diff --git a/apps/bench.py b/apps/bench.py index 6a7aadda..bb30c330 100644 --- a/apps/bench.py +++ b/apps/bench.py @@ -55,6 +55,7 @@ from bumble.sdp import ( from bumble.transport import open_transport_or_link import bumble.rfcomm import bumble.core +from bumble.utils import AsyncRunner # ----------------------------------------------------------------------------- @@ -322,7 +323,7 @@ class Receiver: self.expected_packet_index = packet_index + 1 if packet_flags & PACKET_FLAG_LAST: - asyncio.create_task( + AsyncRunner.run( self.packet_io.send_packet( struct.pack('>bbI', PacketType.ACK, packet_flags, packet_index) ) @@ -403,7 +404,7 @@ class Ping: self.latencies.append(latency) print( color( - f'@@@ Received ACK [{packet_index}], latency={latency:.2f}ms', + f'<<< Received ACK [{packet_index}], latency={latency:.2f}ms', 'green', ) ) @@ -422,7 +423,7 @@ class Ping: self.done.set() return - asyncio.create_task(self.send_next_ping()) + AsyncRunner.run(self.send_next_ping()) # ----------------------------------------------------------------------------- @@ -471,7 +472,7 @@ class Pong: self.expected_packet_index = packet_index + 1 - asyncio.create_task( + AsyncRunner.run( self.packet_io.send_packet( struct.pack('>bbI', PacketType.ACK, packet_flags, packet_index) ) diff --git a/bumble/device.py b/bumble/device.py index f30d2787..dae502c1 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -1245,7 +1245,7 @@ class Device(CompositeEventEmitter): async def power_off(self) -> None: if self.powered_on: - await self.host.reset() + await self.host.flush() self.powered_on = False # TODO: more cleanup diff --git a/bumble/utils.py b/bumble/utils.py index 474fff2f..880d3bbb 100644 --- a/bumble/utils.py +++ b/bumble/utils.py @@ -20,7 +20,7 @@ import logging import traceback import collections import sys -from typing import Awaitable, TypeVar +from typing import Awaitable, Set, TypeVar from functools import wraps from pyee import EventEmitter @@ -157,6 +157,9 @@ class AsyncRunner: # Shared default queue default_queue = WorkQueue() + # Shared set of running tasks + running_tasks: Set[Awaitable] = set() + @staticmethod def run_in_task(queue=None): """ @@ -187,6 +190,19 @@ class AsyncRunner: return decorator + @staticmethod + def run(coroutine): + """ + Create a task to run a coroutine in a "fire and forget" task. + + Using this method instead of just calling `asyncio.create_task(coroutine)` + is necessary when you don't keep a reference to the task, because `asyncio` + only keeps weak references to alive tasks. + """ + task = asyncio.create_task(coroutine) + AsyncRunner.running_tasks.add(task) + task.add_done_callback(AsyncRunner.running_tasks.remove) + # ----------------------------------------------------------------------------- class FlowControlAsyncPipe: diff --git a/docs/mkdocs/src/apps_and_tools/bench.md b/docs/mkdocs/src/apps_and_tools/bench.md index 7c8f37a4..b98d3cdc 100644 --- a/docs/mkdocs/src/apps_and_tools/bench.md +++ b/docs/mkdocs/src/apps_and_tools/bench.md @@ -43,7 +43,7 @@ To test once device against another, one of the two devices must be running the ``peripheral`` command and the other the ``central`` command. The device running the ``peripheral`` command will accept connections from the device running the ``central`` command. -When using Bluetooth LE (all modes except for ``rfcomm-server`` and ``rfcomm`` client), +When using Bluetooth LE (all modes except for ``rfcomm-server`` and ``rfcomm-client``), the default addresses configured in the tool should be sufficient. But when using Bluetooth Classic, the address of the Peripheral must be specified on the Central using the ``--peripheral`` option. The address will be printed by the Peripheral when