close

open suspend override fun close()

Close the socket, waiting first for any in-flight send to finish.

socket.close() cancels the same io.ktor.utils.io.ByteChannel a concurrent writeFully/flush is writing into — and for TLS, hands it to ktor's cio-tls-closer coroutine, which flushes a close_notify record through it. That channel is single-writer: two coroutines mutating its kotlinx.io segment list corrupt it, surfacing as Segment.compact "Check failed." or a NullPointerException inside writeRecord (YouTrack KTOR-7729, open upstream — callers must serialize). Holding sendMutex across the close makes teardown part of the same single-writer discipline as send.

MqttConnection already quiesces its own writer before calling this, so the lock is normally uncontended; the guard matters for direct SPI users and for a close() racing a send() from another caller.

The wait is bounded: a peer that has stopped reading can block writeFully until the TCP timeout, and closing the socket is what unblocks it, so after CLOSE_QUIESCE_TIMEOUT_MS the close proceeds regardless.