Changeset 280 in code for trunk


Ignore:
Timestamp:
Apr 30, 2020, 8:35:02 AM (5 years ago)
Author:
contact
Message:

Use a lock to protect conn.{closed,outgoing}

Unfortunately, I don't think there's a good way to implement net.Conn
semantics on top of channels. The Close and SendMessage methods should
gracefully fail without panicking if the connection is already closed.
Using only channels leads to race conditions.

We could remove the lock if Close and SendMessage are only called from a
single goroutine. However that's not the case right now.

Closes: https://todo.sr.ht/~emersion/soju/55

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/conn.go

    r210 r280  
    44        "fmt"
    55        "net"
     6        "sync"
    67        "time"
    78
     
    2122
    2223type conn struct {
    23         net      net.Conn
    24         irc      *irc.Conn
    25         srv      *Server
    26         logger   Logger
     24        net    net.Conn
     25        irc    *irc.Conn
     26        srv    *Server
     27        logger Logger
     28
     29        lock     sync.Mutex
    2730        outgoing chan<- *irc.Message
    28         closed   chan struct{}
     31        closed   bool
    2932}
    3033
     
    3942                outgoing: outgoing,
    4043                logger:   logger,
    41                 closed:   make(chan struct{}),
    4244        }
    4345
     
    6971
    7072func (c *conn) isClosed() bool {
    71         select {
    72         case <-c.closed:
    73                 return true
    74         default:
    75                 return false
    76         }
     73        c.lock.Lock()
     74        defer c.lock.Unlock()
     75        return c.closed
    7776}
    7877
    7978// Close closes the connection. It is safe to call from any goroutine.
    8079func (c *conn) Close() error {
    81         if c.isClosed() {
     80        c.lock.Lock()
     81        defer c.lock.Unlock()
     82
     83        if c.closed {
    8284                return fmt.Errorf("connection already closed")
    8385        }
    84         close(c.closed)
     86
     87        c.closed = true
    8588        close(c.outgoing)
    8689        return nil
     
    102105// SendMessage queues a new outgoing message. It is safe to call from any
    103106// goroutine.
     107//
     108// If the connection is closed before the message is sent, SendMessage silently
     109// drops the message.
    104110func (c *conn) SendMessage(msg *irc.Message) {
    105         if c.isClosed() {
     111        c.lock.Lock()
     112        defer c.lock.Unlock()
     113
     114        if c.closed {
    106115                return
    107116        }
Note: See TracChangeset for help on using the changeset viewer.