Skip to content

Commit 2eabd63

Browse files
Nakakiyo092claude
andcommitted
Reuse ISO-TP stacks across DIDs of the same addressing scheme
python-can-isotp's TransportLayer is designed for long-lived stacks: start() spins up two daemon threads and initializes internal state (timers, sequence counters, queues) that the library expects to persist across transfers. Building stacks per DID created ~500 threads per functional / broadcast read and discarded the layer's internal state nine times per run. Move stack construction and start()/stop() into _read_all_dids, scoped to each addressing scheme. _read_did becomes a pure send + poll helper. Stacks now live across all three DIDs of their scheme and are torn down exactly three times per run. A try/finally guarantees stop() runs even if a DID's recv loop raises. Header-based filtering in the recv loop already discards any cross-DID residue in the queues, so reusing stacks across DIDs is safe. Test runtime drops from 66.7 s to 22.7 s without any change to message handling or CAN traffic. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent aa75e5c commit 2eabd63

1 file changed

Lines changed: 21 additions & 21 deletions

File tree

src/reader.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,25 @@ def _read_all_dids(args, bus, notifier):
221221
"""Read all EDR DIDs via 11bits functional, 11bits physical, and 29bits addresses."""
222222
for builder in (_build_11func, _build_11phys, _build_29bit):
223223
try:
224-
for did in _EDR_DID_LIST:
225-
tx_stack, rx_stacks, addr_type, mode_label = builder(
226-
bus, notifier, _ISOTP_PARAMS
227-
)
228-
payload = _read_did(
229-
did, tx_stack, rx_stacks, addr_type, mode_label, args.timeout
230-
)
231-
_output_data(payload)
224+
tx_stack, rx_stacks, addr_type, mode_label = builder(
225+
bus, notifier, _ISOTP_PARAMS
226+
)
227+
# The 11bits physical builder returns the same instance as tx_stack and
228+
# rx_stacks[0]; set() dedupes so start() / stop() run once per stack.
229+
# python-can-isotp's TransportLayer is designed for long-lived stacks:
230+
# construct once per scheme, reuse across DIDs, tear down at the end.
231+
all_stacks = {tx_stack, *rx_stacks}
232+
for s in all_stacks:
233+
s.start()
234+
try:
235+
for did in _EDR_DID_LIST:
236+
payload = _read_did(
237+
did, tx_stack, rx_stacks, addr_type, mode_label, args.timeout
238+
)
239+
_output_data(payload)
240+
finally:
241+
for s in all_stacks:
242+
s.stop()
232243
except Exception as err:
233244
print(err)
234245

@@ -255,15 +266,8 @@ def _read_did(did, tx_stack, rx_stacks, addr_type, mode_label,
255266
data=bytes([(did >> 8) & 0xFF, did & 0xFF]) # DID encoded as big-endian 2-byte
256267
)
257268

258-
# In 11bits physical mode tx_stack is also the sole rx_stack (symmetric Address);
259-
# deduplicate so start() / stop() are called once per unique instance.
260-
all_stacks = [tx_stack] + [s for s in rx_stacks if s is not tx_stack]
261-
262-
# Start stacks
263-
for s in all_stacks:
264-
s.start()
265-
266-
# Send request
269+
# Send request. Stacks are started/stopped by the caller (_read_all_dids)
270+
# once per addressing scheme, not per DID.
267271
tx_stack.send(request.get_payload(), addr_type)
268272

269273
try:
@@ -291,10 +295,6 @@ def _read_did(did, tx_stack, rx_stacks, addr_type, mode_label,
291295
print(err)
292296
return None
293297

294-
# Stop stacks
295-
for s in all_stacks:
296-
s.stop()
297-
298298
if payload is not None:
299299
print(len(payload), "bytes of data received.")
300300
else:

0 commit comments

Comments
 (0)