diff --git a/datagram/packetconn.go b/datagram/packetconn.go index 8c14d12..a95f13d 100644 --- a/datagram/packetconn.go +++ b/datagram/packetconn.go @@ -69,16 +69,14 @@ func (c *DatagramConn) Close() error { c.closed = true - // Close reader and writer + // Close reader and writer - these are owned by this connection if c.reader != nil { c.reader.Close() } - // Close the session - if err := c.session.Close(); err != nil { - logger.WithError(err).Error("Failed to close session") - return oops.Errorf("failed to close datagram connection: %w", err) - } + // DO NOT close the session - it's a shared resource that may be used by other connections + // The session should be closed by the code that created it, not by individual connections + // that use it. This follows the principle that the creator owns the resource. logger.Debug("Successfully closed DatagramConn") return nil diff --git a/datagram/read.go b/datagram/read.go index d9836b2..51b1c5d 100644 --- a/datagram/read.go +++ b/datagram/read.go @@ -46,14 +46,21 @@ func (r *DatagramReader) Close() error { logger.Debug("Closing DatagramReader") r.closed = true - // Signal termination to receiveLoop and wait for it to exit + + // Signal termination to receiveLoop close(r.closeChan) - // Give receiveLoop time to detect the close signal and exit - // before closing the channels it might be writing to - time.Sleep(10 * time.Millisecond) + // Wait for receiveLoop to signal it has exited by closing doneChan + // This ensures proper synchronization without arbitrary delays + select { + case <-r.doneChan: + // receiveLoop has confirmed it stopped + case <-time.After(5 * time.Second): + // Timeout protection - log warning but continue cleanup + logger.Warn("Timeout waiting for receive loop to stop") + } - // Now safe to close the receiver channels + // Now safe to close the receiver channels since receiveLoop has stopped close(r.recvChan) close(r.errorChan) @@ -66,6 +73,9 @@ func (r *DatagramReader) receiveLoop() { logger := log.WithField("session_id", r.session.ID()) logger.Debug("Starting receive loop") + // Ensure we signal completion when this loop exits + r.doneChan = make(chan struct{}) + for { // Check for closure in a non-blocking way first select { diff --git a/datagram/types.go b/datagram/types.go index 580f4cc..1435779 100644 --- a/datagram/types.go +++ b/datagram/types.go @@ -23,6 +23,7 @@ type DatagramReader struct { recvChan chan *Datagram errorChan chan error closeChan chan struct{} + doneChan chan struct{} closed bool mu sync.RWMutex }