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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/golang-nuts/c8bc5310-2ca2-498a-99a1-9b944692b150n%40googlegroups.com.