keys(), items(), values() each acquired self.lock then returned a live
dict view (dict_keys, dict_items, dict_values). The lock was released
on return, leaving callers with an unsynchronised view that races with
concurrent tx()/rx()/remove() calls on other threads.
Wrap each return in list() so a snapshot is taken while the lock is held.
Tests added (tests/packets/test_tracker.py):
- test_keys_returns_snapshot_not_view: asserts isinstance list and that
a subsequent mutation does not appear in the returned value
- test_items_returns_snapshot_not_view: same for items()
- test_values_returns_snapshot_not_view: same for values()
Closes#242