Skip to content

Commit d271323

Browse files
authored
Fix iteration in Bus.stop_all_periodic_tasks (#901)
Fix iteration in Bus.stop_all_periodic_tasks so tasks are properly stopped. In addition, add a test in order to verify that Cyclic Tasks are correctly stopped when multiple tasks are active on a particular bus. The test is carried out via the SocketCAN interface. Note: This is a squashed cherry pick of the following commits from develop: - 8112d13 - 1aa0bc6 Addresses #634
1 parent bdf46cd commit d271323

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

can/bus.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,21 @@ def _send_periodic_internal(self, msg, period, duration=None):
247247
return task
248248

249249
def stop_all_periodic_tasks(self, remove_tasks=True):
250-
"""Stop sending any messages that were started using bus.send_periodic
250+
"""Stop sending any messages that were started using **bus.send_periodic**.
251+
252+
.. note::
253+
The result is undefined if a single task throws an exception while being stopped.
251254
252255
:param bool remove_tasks:
253256
Stop tracking the stopped tasks.
254257
"""
255258
for task in self._periodic_tasks:
256-
task.stop(remove_task=remove_tasks)
259+
# we cannot let `task.stop()` modify `self._periodic_tasks` while we are
260+
# iterating over it (#634)
261+
task.stop(remove_task=False)
262+
263+
if remove_tasks:
264+
self._periodic_tasks = []
257265

258266
def __iter__(self):
259267
"""Allow iteration on messages as they are received.

test/test_cyclic_socketcan.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,56 @@ def test_modify_data_message(self):
171171
<= self.DELTA
172172
)
173173

174+
def test_stop_all_periodic_tasks_and_remove_task(self):
175+
message_a = can.Message(
176+
arbitration_id=0x401,
177+
data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11],
178+
is_extended_id=False,
179+
)
180+
message_b = can.Message(
181+
arbitration_id=0x402,
182+
data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22],
183+
is_extended_id=False,
184+
)
185+
message_c = can.Message(
186+
arbitration_id=0x403,
187+
data=[0x33, 0x33, 0x33, 0x33, 0x33, 0x33],
188+
is_extended_id=False,
189+
)
190+
191+
# Start Tasks
192+
task_a = self._send_bus.send_periodic(message_a, self.PERIOD)
193+
task_b = self._send_bus.send_periodic(message_b, self.PERIOD)
194+
task_c = self._send_bus.send_periodic(message_c, self.PERIOD)
195+
196+
self.assertIsInstance(task_a, can.broadcastmanager.ModifiableCyclicTaskABC)
197+
self.assertIsInstance(task_b, can.broadcastmanager.ModifiableCyclicTaskABC)
198+
self.assertIsInstance(task_c, can.broadcastmanager.ModifiableCyclicTaskABC)
199+
200+
for _ in range(6):
201+
_ = self._recv_bus.recv(self.PERIOD)
202+
203+
# Stop all tasks and delete
204+
self._send_bus.stop_all_periodic_tasks(remove_tasks=True)
205+
206+
# Now wait for a few periods, after which we should definitely not
207+
# receive any CAN messages
208+
time.sleep(4 * self.PERIOD)
209+
210+
# If we successfully deleted everything, then we will eventually read
211+
# 0 messages.
212+
successfully_stopped = False
213+
for _ in range(6):
214+
rx_message = self._recv_bus.recv(self.PERIOD)
215+
216+
if rx_message is None:
217+
successfully_stopped = True
218+
break
219+
self.assertTrue(successfully_stopped, "Still received messages after stopping")
220+
221+
# None of the tasks should still be associated with the bus
222+
self.assertEqual(0, len(self._send_bus._periodic_tasks))
223+
174224

175225
if __name__ == "__main__":
176226
unittest.main()

0 commit comments

Comments
 (0)