I'm trying to implement a simple Connection type: type Connection { // Puts a batch of frames into a queue which can later be read. // This method is called only from a single goroutine. PutFrames(frames []*Frame) // Returns a frame from the queue if present, with timeout. // This method is called only from a single goroutine. Read(timeout time.Duration) *Frame }
I cannot change this interface. The simplest solution that I've come up with is the following: ``` type Frame struct{} type Connection struct { sem *semaphore.Weighted // from http://golang.org/x/sync/semaphore mu sync.Mutex frames []*Frame } func NewConnection() *Connection { return &Connection{ sem: semaphore.NewWeighted(1), } } func (c *Connection) getFrames() []*Frame { c.mu.Lock() defer c.mu.Unlock() return c.frames } // Can be called only from one goroutine. func (c *Connection) Read(timeout time.Duration) *Frame { if timeout < 0 { return nil } start := time.Now() c.mu.Lock() if len(c.frames) > 0 { f := c.frames[0] c.frames = c.frames[1:] if len(c.frames) == 0 { // Drain the permit if present. _ = c.sem.TryAcquire(1) } c.mu.Unlock() return f } c.mu.Unlock() if !c.awaitFrames(timeout) { return nil } return c.Read(timeout - time.Since(start)) } func (c *Connection) awaitFrames(timeout time.Duration) bool { done := make(chan struct{}) go func() { _ = c.sem.Acquire(context.Background(), 1) close(done) }() select { case <-time.After(timeout): return false case <-done: return true } } // Can be called only from one goroutine. func (c *Connection) PutFrames(frames []*Frame) { c.mu.Lock() defer c.mu.Unlock() if len(c.frames) == 0 { c.sem.Release(1) } c.frames = append(c.frames, frames...) } ``` The problem with this solution is that it's very complex (let alone inefficient, but I don't care about performance in this case). Also, note that this solution only works because PutFrames() and Read() are only ever called from single goroutines. I'm afraid I would need to hack into Semaphore implementation to make it work for more concurrent callers. If sync.Cond.WaitTimeout() method existed, the code could be made much simpler. I don't understand what Andrew Gerrard suggested in this comment: https://github.com/golang/go/issues/9578#issuecomment-69700942 and how it is supposed to work. In his snippet, cond.Wait() is called when the mutex is not locked. Questions: - Are there any bugs in my solution? - Are there are ways to make my solution simpler? - Is it a good case for justifying adding Cond.WaitTimeout()? -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/c8bc5310-2ca2-498a-99a1-9b944692b150n%40googlegroups.com.