--
A scalable lock-free channel.
- Supports graceful closing.
- Supports blocking and non-blocking operations.
- Supports select.
- Scales with the number of cores.
go get github.com/OneOfOne/lfchan
import (
"fmt"
"github.com/OneOfOne/lfchan"
)
func main() {
ch := lfchan.New() // or
// ch := lfchan.NewSize(10) // buffered channel
go ch.Send("hello", true)
fmt.Printf("%s world", ch.Recv(true).(string))
}
go run "$GOPATH/src/github.com/OneOfOne/lfchan/gen.go" type [pkgName or . to embed the chan in the current package]
# primitve type
go run "$GOPATH/src/github.com/OneOfOne/lfchan/gen.go" string internal/stringChan
# or for using a non-native type
go run "$GOPATH/src/github.com/OneOfOne/lfchan/gen.go" github.com/OneOfOne/cmap.CMap internal/cmapChan
go run "$GOPATH/src/github.com/OneOfOne/lfchan/gen.go" github.com/OneOfOne/cmap.CMap
package main
// go run "$GOPATH/src/github.com/OneOfOne/lfchan/gen.go" string internal/stringChan
import (
"fmt"
"github.com/YOU/internal/stringChan"
)
func main() {
ch := stringChan.New() // or
// ch := stringChan.NewSize(10) // buffered channel
go func() {
go ch.Send("lfchan", true)
ch.Send("hello", true)
}()
for s, ok := ch.Recv(true); ok; s, ok = ch.Recv(true) {
fmt.Print(s, " ")
}
fmt.Println()
}
package main
// go run "$GOPATH/src/github.com/OneOfOne/lfchan/gen.go" "[]*node" .
import (
"fmt"
)
type node struct {
v int
}
func main() {
// notice how for embeded types the new func is called "new[Size]{TypeName}Chan()
ch := newNodeChan() // or
// ch := newSizeNodeChan(10) // buffered channel
go func() {
for i := 0; i < 10; i++ {
ch.Send([]*Node{{i}, {i*i}}, true)
}
}()
for ns, ok := ch.Recv(true); ok; ns, ok = ch.Recv(true) {
for i, n := range ns {
fmt.Println(i, n.v)
}
}
}
-
Doesn't scale correctly on true SMP systems (issue #3).
-
Under high concurrency, ch.Len() can return -1 (issue #2)Fixed by commit bdddd90. -
typed channels can't handle zero value primitve types correctly, for example it can't handle sending 0 on an int channelFixed. -
gen.go can't handle maps to non-native types.
# Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
# Linux 4.4.5 x86_64
➜ go test -bench=. -benchmem -cpu 1,4,8 -benchtime 10s
BenchmarkLFChan 100000000 157 ns/op 8 B/op 1 allocs/op
BenchmarkLFChan-4 100000000 187 ns/op 8 B/op 1 allocs/op
BenchmarkLFChan-8 100000000 168 ns/op 8 B/op 1 allocs/op
BenchmarkChan 200000000 96.2 ns/op 8 B/op 1 allocs/op
BenchmarkChan-4 50000000 244 ns/op 8 B/op 1 allocs/op
BenchmarkChan-8 50000000 323 ns/op 8 B/op 1 allocs/op
PASS
ok github.com/OneOfOne/lfchan 124.067s
check issue #3 for more benchmarks and updates.
- Sadly, it is the only clean way to release the scheduler in a tight loop, Go doesn't provide any other way to yield,
time.Sleep
causes random allocations at times.sync/atomic.Value
has access to internal funcs which can control the scheduler, however user code can't do that.
- Yes and no, using the spinlock and few sleeps in the code makes it very efficient even under idle conditions.
This project is released under the Apache v2. licence. See LICENCE for more details.