mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-06-08 06:01:55 -07:00
794e437f6d
RNSChannelOutlet.send() can return a packet that never reached the wire (link not ACTIVE, no capable interface, etc). The old Channel.send() queued the envelope in _tx_ring before calling outlet.send(), then tried to rewind _next_sequence and remove the envelope if the outlet returned a failed packet. Two problems: - Between queueing and outlet.send() returning, _tx_ring held an envelope with packet.raw=None. Any worker thread iterating the ring (timeout fire, proof callback) crashed in get_packet_id's packet.get_hash() with a TypeError on None.raw. - The rewind was only safe for a single-threaded sender: it checked "is _next_sequence one past mine?" and skipped the rewind otherwise. Under concurrent senders, the rewind silently failed, leaving a hole in the on-wire sequence stream. The receiver's contiguous seqnum rule then stalled the channel permanently with no error. This fix serializes the reservation-and-transmit pair with a per-channel _send_lock so the rewind is always correct, and defers queueing until outlet.send() returns a real packet so _tx_ring never contains a packet-less envelope. _packet_tx_op() and get_packet_id() now also defensively skip/return-None for packet-less envelopes. Also handle the small race where a proof arrives between outlet.send() registering the receipt and us installing the delivery callback: after registration, re-read the receipt status and synthesize the _packet_delivered() call if it's already DELIVERED.