@@ -253,7 +253,7 @@ static inline lwip_tcp_event_packet_t *_get_async_event() {
253
253
}
254
254
}
255
255
256
- static void _remove_events_for_client (AsyncClient *client) {
256
+ static size_t _remove_events_for_client (AsyncClient *client) {
257
257
lwip_tcp_event_packet_t *removed_event_chain;
258
258
{
259
259
queue_mutex_guard guard;
@@ -269,6 +269,7 @@ static void _remove_events_for_client(AsyncClient *client) {
269
269
removed_event_chain = t->next ;
270
270
_free_event (t);
271
271
}
272
+ return count;
272
273
};
273
274
274
275
void AsyncTCP_detail::handle_async_event (lwip_tcp_event_packet_t *e) {
@@ -484,7 +485,8 @@ void AsyncTCP_detail::tcp_error(void *arg, int8_t err) {
484
485
// ets_printf("+E: 0x%08x\n", arg);
485
486
AsyncClient *client = reinterpret_cast <AsyncClient *>(arg);
486
487
if (client && client->_pcb ) {
487
- _reset_tcp_callbacks (client->_pcb , client);
488
+ // The pcb has already been freed by LwIP; do not attempt to clear the callbacks!
489
+ _remove_events_for_client (client);
488
490
client->_pcb = nullptr ;
489
491
}
490
492
@@ -615,26 +617,35 @@ static esp_err_t _tcp_recved(tcp_pcb **pcb, size_t len) {
615
617
}
616
618
617
619
static err_t _tcp_close_api (struct tcpip_api_call_data *api_call_msg) {
620
+ // Unlike the other calls, this is not a direct wrapper of the LwIP function;
621
+ // we perform the AsyncClient teardown interlocked safely with the LwIP task.
622
+
623
+ // As a postcondition, the queue must not have any events referencing
624
+ // the AsyncClient in api_call_msg->close. This is because it is possible for
625
+ // an error event to have been queued, clearing the pcb*, but after the async
626
+ // thread has committed to closing/destructing the AsyncClient object.
627
+
618
628
tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg;
619
629
msg->err = ERR_CONN;
620
630
if (*msg->pcb ) {
621
- // Unlike the other calls, this is not a direct wrapper of the LwIP function;
622
- // we perform the AsyncClient teardown interlocked safely with the LwIP task.
623
631
tcp_pcb *pcb = *msg->pcb ;
624
632
_reset_tcp_callbacks (pcb, msg->close );
625
- msg-> err = tcp_close (pcb);
626
- if (msg-> err != ERR_OK) {
633
+ if ( tcp_close (pcb) != ERR_OK) {
634
+ // We do not permit failure here: abandon the pcb anyways.
627
635
tcp_abort (pcb);
628
636
}
637
+ msg->err = ERR_OK;
629
638
*msg->pcb = nullptr ; // PCB is now the property of LwIP
639
+ } else {
640
+ // Ensure there is not an error event queued for this client
641
+ if (_remove_events_for_client (msg->close )) {
642
+ msg->err = ERR_OK; // dispose needs to be run
643
+ }
630
644
}
631
645
return msg->err ;
632
646
}
633
647
634
648
static esp_err_t _tcp_close (tcp_pcb **pcb, AsyncClient *client) {
635
- if (!pcb || !*pcb) {
636
- return ERR_CONN;
637
- }
638
649
tcp_api_call_t msg;
639
650
msg.pcb = pcb;
640
651
msg.close = client;
@@ -643,21 +654,29 @@ static esp_err_t _tcp_close(tcp_pcb **pcb, AsyncClient *client) {
643
654
}
644
655
645
656
static err_t _tcp_abort_api (struct tcpip_api_call_data *api_call_msg) {
657
+ // Like close(), we must ensure that the queue is cleared
646
658
tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg;
647
659
msg->err = ERR_CONN;
648
660
if (*msg->pcb ) {
649
- tcp_abort (*msg->pcb );
661
+ tcp_pcb *pcb = *msg->pcb ;
662
+ _reset_tcp_callbacks (pcb, msg->close );
663
+ tcp_abort (pcb);
650
664
*msg->pcb = nullptr ; // PCB is now the property of LwIP
665
+ msg->err = ERR_OK;
666
+ } else {
667
+ // Ensure there is not an error event queued for this client
668
+ _remove_events_for_client (msg->close );
651
669
}
652
670
return msg->err ;
653
671
}
654
672
655
- static esp_err_t _tcp_abort (tcp_pcb **pcb) {
673
+ static esp_err_t _tcp_abort (tcp_pcb **pcb, AsyncClient *client ) {
656
674
if (!pcb || !*pcb) {
657
675
return ERR_CONN;
658
676
}
659
677
tcp_api_call_t msg;
660
678
msg.pcb = pcb;
679
+ msg.close = client;
661
680
tcpip_api_call (_tcp_abort_api, (struct tcpip_api_call_data *)&msg);
662
681
return msg.err ;
663
682
}
@@ -903,7 +922,7 @@ void AsyncClient::close(bool now) {
903
922
904
923
int8_t AsyncClient::abort () {
905
924
if (_pcb) {
906
- _tcp_abort (&_pcb);
925
+ _tcp_abort (&_pcb, this );
907
926
// _pcb is now NULL
908
927
}
909
928
return ERR_ABRT;
@@ -968,13 +987,11 @@ void AsyncClient::ackPacket(struct pbuf *pb) {
968
987
969
988
int8_t AsyncClient::_close () {
970
989
// ets_printf("X: 0x%08x\n", (uint32_t)this);
971
- int8_t err = ERR_OK;
972
- if (_pcb) {
973
- _tcp_close (&_pcb, this );
974
- // _pcb is now NULL
975
- if (_discard_cb) {
976
- _discard_cb (_discard_cb_arg, this );
977
- }
990
+ int8_t err = _tcp_close (&_pcb, this );
991
+ // _pcb is now NULL
992
+ if ((err == ERR_OK) && _discard_cb) {
993
+ // _pcb was closed here
994
+ _discard_cb (_discard_cb_arg, this );
978
995
}
979
996
return err;
980
997
}
0 commit comments