This is because:
lwip_close() -> netconn_delete() -> API_MSG -> do_delconn() -> do_close_internal() -> tcp_close() -> tcp_close_shutdown() -> tcp_send_fin() -> tcp_enqueue_flags()
That last function checks the TCP send queue and returns with ERR_MEM when the cable is unplugged because the send queue is full.
Thus, that error is given up to do_close_internal() which does NOT sys_sem_signal() the op_completed semaphore and in turn, the API_MSG is still being waited on.
Do_close_internal() sets poll_tcp and sent_tcp callbacks which re-call do_close_interal() until it works (when the cable is re-plugged and the send queue can be worked on).
This is because:
lwip_close() -> netconn_delete() -> API_MSG -> do_delconn() -> do_close_internal() -> tcp_close() -> tcp_close_shutdown() -> tcp_send_fin() -> tcp_enqueue_flags()
That last function checks the TCP send queue and returns with ERR_MEM when the cable is unplugged because the send queue is full.
Thus, that error is given up to do_close_internal() which does NOT sys_sem_signal() the op_completed semaphore and in turn, the API_MSG is still being waited on.
Do_close_internal() sets poll_tcp and sent_tcp callbacks which re-call do_close_interal() until it works (when the cable is re-plugged and the send queue can be worked on).