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.

Reply via email to