Skip to content

Commit

Permalink
BusABC.recv: keep calling _recv_internal until it returns None
Browse files Browse the repository at this point in the history
Even if recv() is called with timeout=0, the caller's intention is
probably for recv() to check all of the messages that have already
arrived at the interface until one of them matches the filters.

This is already the way recv() behaves for interface drivers that take
advantage of hardware or OS-level filtering, but those that use BusABC's
default software-based filtering might return None even if a matching
message has already arrived.
  • Loading branch information
malsyned committed Oct 26, 2023
1 parent 38c4dc4 commit fc7e8c2
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 15 deletions.
23 changes: 8 additions & 15 deletions can/bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,26 +119,19 @@ def recv(self, timeout: Optional[float] = None) -> Optional[Message]:
while True:
# try to get a message
msg, already_filtered = self._recv_internal(timeout=time_left)
time_left = timeout - (time() - start)

if msg is None:
# if timeout is None or there is still time, try again
if timeout is None or time_left > 0:
continue
return None

# return it, if it matches
if msg and (already_filtered or self._matches_filters(msg)):
if already_filtered or self._matches_filters(msg):
LOG.log(self.RECV_LOGGING_LEVEL, "Received: %s", msg)
return msg

# if not, and timeout is None, try indefinitely
elif timeout is None:
continue

# try next one only if there still is time, and with
# reduced timeout
else:
time_left = timeout - (time() - start)

if time_left > 0:
continue

return None

def _recv_internal(
self, timeout: Optional[float]
) -> Tuple[Optional[Message], bool]:
Expand Down
11 changes: 11 additions & 0 deletions test/test_message_filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ def test_match_example_message(self):
self.assertFalse(self.bus._matches_filters(EXAMPLE_MSG))
self.assertTrue(self.bus._matches_filters(HIGHEST_MSG))

def test_empty_queue_up_to_match(self):
self.bus.set_filters(MATCH_EXAMPLE)
bus2 = Bus(interface="virtual", channel="testy")
bus2.send(HIGHEST_MSG)
bus2.send(EXAMPLE_MSG)
actual = self.bus.recv(timeout=0)
self.assertTrue(EXAMPLE_MSG.equals(actual,
timestamp_delta=None,
check_direction=False,
check_channel=False))


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

0 comments on commit fc7e8c2

Please sign in to comment.