Skip to content

Commit f822afa

Browse files
authored
55 scan tasks should be stopped cleanly when process stopped (#75)
teardown tasks when the backend is deleted and print error in failing scan tasks
1 parent bdab4fa commit f822afa

File tree

2 files changed

+31
-21
lines changed

2 files changed

+31
-21
lines changed

src/fastcs/backend.py

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import asyncio
22
from collections import defaultdict
33
from collections.abc import Callable
4-
from concurrent.futures import Future
54
from types import MethodType
65

76
from softioc.asyncio_dispatcher import AsyncioDispatcher
@@ -21,7 +20,7 @@ def __init__(
2120
self._controller = controller
2221

2322
self._initial_tasks = [controller.connect]
24-
self._scan_tasks: list[Future] = []
23+
self._scan_tasks: list[asyncio.Task] = []
2524

2625
asyncio.run_coroutine_threadsafe(
2726
self._controller.initialise(), self._loop
@@ -41,22 +40,31 @@ def _link_process_tasks(self):
4140
_link_single_controller_put_tasks(single_mapping)
4241
_link_attribute_sender_class(single_mapping)
4342

43+
def __del__(self):
44+
self.stop_scan_tasks()
45+
4446
def run(self):
4547
self._run_initial_tasks()
46-
self._start_scan_tasks()
47-
48+
self.start_scan_tasks()
4849
self._run()
4950

5051
def _run_initial_tasks(self):
5152
for task in self._initial_tasks:
5253
future = asyncio.run_coroutine_threadsafe(task(), self._loop)
5354
future.result()
5455

55-
def _start_scan_tasks(self):
56-
scan_tasks = _get_scan_tasks(self._mapping)
56+
def start_scan_tasks(self):
57+
self._scan_tasks = [
58+
self._loop.create_task(coro()) for coro in _get_scan_coros(self._mapping)
59+
]
5760

58-
for task in scan_tasks:
59-
asyncio.run_coroutine_threadsafe(task(), self._loop)
61+
def stop_scan_tasks(self):
62+
for task in self._scan_tasks:
63+
if not task.done():
64+
try:
65+
task.cancel()
66+
except asyncio.CancelledError:
67+
pass
6068

6169
def _run(self):
6270
raise NotImplementedError("Specific Backend must implement _run")
@@ -98,15 +106,15 @@ async def callback(value):
98106
return callback
99107

100108

101-
def _get_scan_tasks(mapping: Mapping) -> list[Callable]:
109+
def _get_scan_coros(mapping: Mapping) -> list[Callable]:
102110
scan_dict: dict[float, list[Callable]] = defaultdict(list)
103111

104112
for single_mapping in mapping.get_controller_mappings():
105113
_add_scan_method_tasks(scan_dict, single_mapping)
106114
_add_attribute_updater_tasks(scan_dict, single_mapping)
107115

108-
scan_tasks = _get_periodic_scan_tasks(scan_dict)
109-
return scan_tasks
116+
scan_coros = _get_periodic_scan_coros(scan_dict)
117+
return scan_coros
110118

111119

112120
def _add_scan_method_tasks(
@@ -144,18 +152,18 @@ async def callback():
144152
return callback
145153

146154

147-
def _get_periodic_scan_tasks(scan_dict: dict[float, list[Callable]]) -> list[Callable]:
148-
periodic_scan_tasks: list[Callable] = []
155+
def _get_periodic_scan_coros(scan_dict: dict[float, list[Callable]]) -> list[Callable]:
156+
periodic_scan_coros: list[Callable] = []
149157
for period, methods in scan_dict.items():
150-
periodic_scan_tasks.append(_create_periodic_scan_task(period, methods))
158+
periodic_scan_coros.append(_create_periodic_scan_coro(period, methods))
151159

152-
return periodic_scan_tasks
160+
return periodic_scan_coros
153161

154162

155-
def _create_periodic_scan_task(period, methods: list[Callable]) -> Callable:
156-
async def scan_task() -> None:
163+
def _create_periodic_scan_coro(period, methods: list[Callable]) -> Callable:
164+
async def scan_coro() -> None:
157165
while True:
158166
await asyncio.gather(*[method() for method in methods])
159167
await asyncio.sleep(period)
160168

161-
return scan_task
169+
return scan_coro

tests/test_backend.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from time import sleep
1+
import asyncio
22

33
import pytest
44

@@ -16,7 +16,7 @@ async def init_task(self):
1616
self.init_task_called = True
1717

1818
def _run(self):
19-
pass
19+
asyncio.run_coroutine_threadsafe(asyncio.sleep(0.3), self._loop)
2020

2121

2222
@pytest.mark.asyncio
@@ -41,5 +41,7 @@ async def test_backend(controller):
4141
# Scan tasks should be running
4242
for _ in range(3):
4343
count = controller.count
44-
sleep(0.05)
44+
await asyncio.sleep(0.1)
4545
assert controller.count > count
46+
47+
backend.stop_scan_tasks()

0 commit comments

Comments
 (0)