Skip to content

Commit 3b8bb3c

Browse files
committed
Add parameter for on_error to BusABC.send_periodic
If there is an error while sending messages in a (background) periodic task, then the `on_error` callback is called, if set. If the callback is configured and returns `True` then the task continues, otherwise it is aborted. While it is possible to update a task with a callback after the task has been created, this procedure is prone to a race condition where the callback might not be configured in time for the first send event. Thus, if the first send fails and the callback has not yet been configured, the task will abort. This commit solves the race condition issue by adding an argument to `BusABC.send_periodic` to specify the callback. By including the callback in the constructor it will be deterministically active for all sends in the task. This fixes issue #1282. This commit also adds myself to the CONTRIBUTORS list.
1 parent ebae360 commit 3b8bb3c

File tree

2 files changed

+18
-4
lines changed

2 files changed

+18
-4
lines changed

CONTRIBUTORS.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,5 @@ Felix Nieuwenhuizen
8181
@fjburgos
8282
@pkess
8383
@felixn
84-
@Tbruno25
84+
@Tbruno25
85+
@andebjor

can/bus.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Contains the ABC bus implementation and its documentation.
33
"""
44

5-
from typing import cast, Any, Iterator, List, Optional, Sequence, Tuple, Union
5+
from typing import cast, Any, Callable, Iterator, List, Optional, Sequence, Tuple, Union
66

77
import can.typechecking
88

@@ -181,6 +181,7 @@ def send_periodic(
181181
period: float,
182182
duration: Optional[float] = None,
183183
store_task: bool = True,
184+
on_error: Optional[Callable[[Exception], bool]] = None,
184185
) -> can.broadcastmanager.CyclicSendTaskABC:
185186
"""Start sending messages at a given period on this bus.
186187
@@ -191,6 +192,9 @@ def send_periodic(
191192
- the Bus instance is shutdown
192193
- :meth:`BusABC.stop_all_periodic_tasks()` is called
193194
- the task's :meth:`CyclicTask.stop()` method is called.
195+
- an error while sending and the (optional) `on_error` callback does
196+
not return `True`. If the callback is not specified the task is
197+
deactivated on error.
194198
195199
:param msgs:
196200
Message(s) to transmit
@@ -202,6 +206,10 @@ def send_periodic(
202206
:param store_task:
203207
If True (the default) the task will be attached to this Bus instance.
204208
Disable to instead manage tasks manually.
209+
:param on_error:
210+
Callable that accepts an exception if any error happened on a `bus`
211+
while sending `msgs`, it shall return either ``True`` or ``False``
212+
depending on desired behaviour of `ThreadBasedCyclicSendTask`.
205213
:return:
206214
A started task instance. Note the task can be stopped (and depending on
207215
the backend modified) by calling the task's :meth:`stop` method.
@@ -232,7 +240,7 @@ def send_periodic(
232240
# Create a backend specific task; will be patched to a _SelfRemovingCyclicTask later
233241
task = cast(
234242
_SelfRemovingCyclicTask,
235-
self._send_periodic_internal(msgs, period, duration),
243+
self._send_periodic_internal(msgs, period, duration, on_error),
236244
)
237245

238246
# we wrap the task's stop method to also remove it from the Bus's list of tasks
@@ -260,6 +268,7 @@ def _send_periodic_internal(
260268
msgs: Union[Sequence[Message], Message],
261269
period: float,
262270
duration: Optional[float] = None,
271+
on_error: Optional[Callable[[Exception], bool]] = None,
263272
) -> can.broadcastmanager.CyclicSendTaskABC:
264273
"""Default implementation of periodic message sending using threading.
265274
@@ -272,6 +281,10 @@ def _send_periodic_internal(
272281
:param duration:
273282
The duration between sending each message at the given rate. If
274283
no duration is provided, the task will continue indefinitely.
284+
:param on_error:
285+
Callable that accepts an exception if any error happened on a `bus`
286+
while sending `msgs`, it shall return either ``True`` or ``False``
287+
depending on desired behaviour of `ThreadBasedCyclicSendTask`.
275288
:return:
276289
A started task instance. Note the task can be stopped (and
277290
depending on the backend modified) by calling the :meth:`stop`
@@ -283,7 +296,7 @@ def _send_periodic_internal(
283296
threading.Lock()
284297
)
285298
task = ThreadBasedCyclicSendTask(
286-
self, self._lock_send_periodic, msgs, period, duration
299+
self, self._lock_send_periodic, msgs, period, duration, on_error
287300
)
288301
return task
289302

0 commit comments

Comments
 (0)