Here's my lock-free channel impl lfchan,
I didn't want to copy/paste the code here since it is multiple files.
I'm trying to get an idea of what can be improved or if there are any bugs in it.
chan.go
type innerChan struct {
q []aValue
sendIdx uint32
recvIdx uint32
slen uint32
rlen uint32
die uint32
}
// Chan is a lock free channel that supports concurrent channel operations.
type Chan struct {
*innerChan
}
// New returns a new channel with the buffer set to 1
func New() Chan {
return NewSize(1)
}
// NewSize creates a buffered channel, with minimum length of 1
func NewSize(sz int) Chan {
if sz < 1 {
panic("sz < 1")
}
return Chan{&innerChan{
q: make([]aValue, sz),
sendIdx: ^uint32(0),
recvIdx: ^uint32(0),
}}
}
// Send adds v to the buffer of the channel and returns true, if the channel is closed it returns false
func (ch Chan) Send(v interface{}, block bool) bool {
if !block && ch.Len() == ch.Cap() {
return false
}
ln, cnt := uint32(len(ch.q)), uint32(0)
for !ch.Closed() {
if ch.Len() == ch.Cap() {
if !block {
return false
}
runtime.Gosched()
continue
}
i := atomic.AddUint32(&ch.sendIdx, 1)
if ch.q[i%ln].CompareAndSwapIfNil(v) {
atomic.AddUint32(&ch.slen, 1)
return true
}
if block {
if i%250 == 0 {
pause(1)
}
} else if cnt++; cnt == ln {
break
}
runtime.Gosched()
}
return false
}
// Recv blocks until a value is available and returns v, true, or if the channel is closed and
// the buffer is empty, it will return nil, false
func (ch Chan) Recv(block bool) (interface{}, bool) {
if !block && ch.Len() == 0 { // fast path
return nilValue, false
}
ln, cnt := uint32(len(ch.q)), uint32(0)
for !ch.Closed() || ch.Len() > 0 {
if ch.Len() == 0 {
if !block {
return nilValue, false
}
runtime.Gosched()
continue
}
i := atomic.AddUint32(&ch.recvIdx, 1)
if v, ok := ch.q[i%ln].SwapWithNil(); ok {
atomic.AddUint32(&ch.rlen, 1)
return v, true
}
if block {
if i%250 == 0 {
pause(1)
}
} else if cnt++; cnt == ln {
break
}
runtime.Gosched()
}
return nilValue, false
}