Address review: retry a dropped freed-slot notification too

The connections-free retry left its sibling asymmetric: on the same
full buffer the slot could report free while the device still read as
connected, with nothing resending the connected=false. A one-deep latch
(address + error) drains at the same 100 ms cadence and re-latches on a
repeat failure; a re-reservation of the address clears it, since the
client re-requesting the connect has already acted on the disconnect
and a late resend would shadow the new connection. A lost
connected=true stays client-timeout territory.
This commit is contained in:
J. Nick Koston
2026-08-09 16:24:03 -05:00
parent 735f810f08
commit 9a5aab63d5
2 changed files with 22 additions and 1 deletions
@@ -206,6 +206,11 @@ BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool rese
return connection;
if (reserve && conn_addr == 0) {
if (address == this->pending_disconnection_address_) {
// The client re-requested this address: it already acted on the
// disconnect, and a late resend would shadow the new connection.
this->pending_disconnection_address_ = 0;
}
connection->send_service_ = INIT_SENDING_SERVICES;
connection->set_address(address);
// All connections must start at INIT
@@ -487,6 +492,13 @@ void BluetoothProxy::loop() {
return;
this->last_advertisement_flush_time_ = now;
if (this->pending_disconnection_address_ != 0 && this->api_connection_ != nullptr) {
// Paced resend of a dropped freed-slot notification; a repeat failure
// re-latches through send_device_connection itself.
uint64_t addr = this->pending_disconnection_address_;
this->pending_disconnection_address_ = 0;
this->send_device_connection(addr, false, 0, this->pending_disconnection_error_);
}
if (this->connections_free_pending_ && this->api_connection_ != nullptr) {
// Resend a dropped slot-state update, paced by the 100 ms gate so the
// retry does not hammer the congestion it exists to survive; the
@@ -633,7 +645,12 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui
call.connected = connected;
call.mtu = mtu;
call.error = error;
this->api_connection_->send_message(call);
if (!this->api_connection_->send_message(call) && !connected) {
// The freed-slot notification must eventually arrive; a lost
// connected=true is covered by the client's own connect timeout.
this->pending_disconnection_address_ = address;
this->pending_disconnection_error_ = error;
}
}
void BluetoothProxy::send_connections_free() {
if (this->api_connection_ != nullptr) {
@@ -251,6 +251,10 @@ class BluetoothProxy final : public Component {
// Pre-allocated response message - always ready to send
api::BluetoothConnectionsFreeResponse connections_free_response_;
// One-deep retry latch for a dropped connected=false notification (the
// freed-slot half of the pair the connections-free retry covers).
uint64_t pending_disconnection_address_{0};
conn_err_t pending_disconnection_error_{0};
// Group 4: 1-byte types grouped together
bool active_;