Skip to content

Commit 628b4fe

Browse files
committed
AsyncUDP: keep track of the last sent packet and skip it while LOOPing
This means we don't invoke the receive handler for packets we send.
1 parent 6789710 commit 628b4fe

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

cores/portduino/AsyncUDP.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,20 @@ void _asyncudp_on_read_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
4949
}
5050

5151
void AsyncUDP::_DO_NOT_CALL_uv_on_read(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
52+
if (nread <= 0) {
53+
return;
54+
}
5255
_handlerMutex.lock();
5356
auto h = _handler;
5457
_handlerMutex.unlock();
55-
if (h) {
56-
AsyncUDPPacket packet((uint8_t*)buf->base, nread);
57-
h(packet);
58+
if (_waitingToBeLooped && nread == _waitingToBeLooped->len && memcmp(buf->base, _waitingToBeLooped->data, nread) == 0) {
59+
_waitingToBeLooped = std::unique_ptr<asyncUDPSendTask>();
60+
_attemptWrite();
61+
} else {
62+
if (h) {
63+
AsyncUDPPacket packet((uint8_t*)buf->base, nread);
64+
h(packet);
65+
}
5866
}
5967
free(buf->base);
6068
}
@@ -156,16 +164,21 @@ size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPAddress addr,
156164
return len;
157165
}
158166

159-
void AsyncUDP::_DO_NOT_CALL_async_cb() {
167+
void AsyncUDP::_attemptWrite() {
160168
_sendQueueMutex.lock();
161-
while (!_sendQueue.empty()) {
169+
if (!_waitingToBeLooped && !_sendQueue.empty()) {
162170
auto task = std::move(_sendQueue.back());
163171
_sendQueue.pop_back();
164172
_sendQueueMutex.unlock();
165173
_doWrite(task->data, task->len, task->addr, task->port);
166174
_sendQueueMutex.lock();
175+
_waitingToBeLooped = std::move(task);
167176
}
168177
_sendQueueMutex.unlock();
178+
}
179+
180+
void AsyncUDP::_DO_NOT_CALL_async_cb() {
181+
_attemptWrite();
169182
if (_quit.load()) {
170183
uv_udp_recv_stop(&_socket);
171184
// FIXME: don't do bytes → string → bytes IP conversion

cores/portduino/AsyncUDP.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class AsyncUDP final
6969
// the queue is used because uv_udp_send is not threadsafe and uv_async can merge multiple calls into one callback
7070
std::vector<std::unique_ptr<asyncUDPSendTask>> _sendQueue;
7171

72+
// _waitingToBeLooped is used to wait for a sent packet to be looped back before we send an other one.
73+
// This allows the recv callback to omit sent packets.
74+
// It must be accessed from the uv loop.
75+
std::unique_ptr<asyncUDPSendTask> _waitingToBeLooped;
76+
7277
std::atomic<bool> _quit;
7378
std::thread _ioThread;
7479

@@ -109,6 +114,9 @@ class AsyncUDP final
109114
void _DO_NOT_CALL_async_cb();
110115

111116
private:
117+
// _attemptWrite must be accessed from the uv loop.
118+
void _attemptWrite();
119+
// _doWrite must be accessed from the uv loop.
112120
void _doWrite(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port);
113121
};
114122

0 commit comments

Comments
 (0)