Skip to content

Commit f2ec361

Browse files
committed
Additional close semantic fixes
When close is invoked, make sure to interlock with the LwIP task and clear the queue; checks of pcb are not safe as it is owned by the LwIP task. Extend this checking to include abort(), as well.
1 parent cc3f80a commit f2ec361

1 file changed

Lines changed: 22 additions & 18 deletions

File tree

src/AsyncTCP.cpp

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -619,19 +619,20 @@ static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg) {
619619
// we perform the AsyncClient teardown interlocked safely with the LwIP task.
620620

621621
// As a postcondition, the queue must not have any events referencing
622-
// this AsyncClient. This is because it is possible for an error event
623-
// to have been queued, clearing the pcb*, but after the async thread
624-
// has committed to closing/destructing the AsyncClient object.
622+
// the AsyncClient in api_call_msg->close. This is because it is possible for
623+
// an error event to have been queued, clearing the pcb*, but after the async
624+
// thread has committed to closing/destructing the AsyncClient object.
625625

626626
tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg;
627627
msg->err = ERR_CONN;
628628
if (*msg->pcb) {
629629
tcp_pcb *pcb = *msg->pcb;
630630
_reset_tcp_callbacks(pcb, msg->close);
631-
msg->err = tcp_close(pcb);
632-
if (msg->err != ERR_OK) {
631+
if (tcp_close(pcb) != ERR_OK) {
632+
// We do not permit failure here: abandon the pcb anyways.
633633
tcp_abort(pcb);
634634
}
635+
msg->err = ERR_OK;
635636
*msg->pcb = nullptr; // PCB is now the property of LwIP
636637
} else {
637638
// Ensure there is not an error event queued for this client
@@ -641,9 +642,6 @@ static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg) {
641642
}
642643

643644
static esp_err_t _tcp_close(tcp_pcb **pcb, AsyncClient *client) {
644-
if (!pcb || !*pcb) {
645-
return ERR_CONN;
646-
}
647645
tcp_api_call_t msg;
648646
msg.pcb = pcb;
649647
msg.close = client;
@@ -652,21 +650,29 @@ static esp_err_t _tcp_close(tcp_pcb **pcb, AsyncClient *client) {
652650
}
653651

654652
static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg) {
653+
// Like close(), we must ensure that the queue is cleared
655654
tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg;
656655
msg->err = ERR_CONN;
657656
if (*msg->pcb) {
658-
tcp_abort(*msg->pcb);
657+
tcp_pcb *pcb = *msg->pcb;
658+
_reset_tcp_callbacks(pcb, msg->close);
659+
tcp_abort(pcb);
659660
*msg->pcb = nullptr; // PCB is now the property of LwIP
661+
msg->err = ERR_OK;
662+
} else {
663+
// Ensure there is not an error event queued for this client
664+
_remove_events_for_client(msg->close);
660665
}
661666
return msg->err;
662667
}
663668

664-
static esp_err_t _tcp_abort(tcp_pcb **pcb) {
669+
static esp_err_t _tcp_abort(tcp_pcb **pcb, AsyncClient *client) {
665670
if (!pcb || !*pcb) {
666671
return ERR_CONN;
667672
}
668673
tcp_api_call_t msg;
669674
msg.pcb = pcb;
675+
msg.close = client;
670676
tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data *)&msg);
671677
return msg.err;
672678
}
@@ -912,7 +918,7 @@ void AsyncClient::close(bool now) {
912918

913919
int8_t AsyncClient::abort() {
914920
if (_pcb) {
915-
_tcp_abort(&_pcb);
921+
_tcp_abort(&_pcb, this);
916922
// _pcb is now NULL
917923
}
918924
return ERR_ABRT;
@@ -977,13 +983,11 @@ void AsyncClient::ackPacket(struct pbuf *pb) {
977983

978984
int8_t AsyncClient::_close() {
979985
// ets_printf("X: 0x%08x\n", (uint32_t)this);
980-
int8_t err = ERR_OK;
981-
if (_pcb) {
982-
_tcp_close(&_pcb, this);
983-
// _pcb is now NULL
984-
if (_discard_cb) {
985-
_discard_cb(_discard_cb_arg, this);
986-
}
986+
int8_t err = _tcp_close(&_pcb, this);
987+
// _pcb is now NULL
988+
if ((err == ERR_OK) && _discard_cb) {
989+
// _pcb was closed here
990+
_discard_cb(_discard_cb_arg, this);
987991
}
988992
return err;
989993
}

0 commit comments

Comments
 (0)