Improved blocking (#513)

core: major improvements to blocking, including support for buffered channels.
This commit is contained in:
Jaden Weiss
2019-09-22 11:58:00 -04:00
committed by Ron Evans
parent d17f500c8b
commit d843ebfe40
12 changed files with 1069 additions and 486 deletions
+104 -8
View File
@@ -1,65 +1,110 @@
package main
import "time"
import (
"time"
"runtime"
)
// waitGroup is a small type reimplementing some of the behavior of sync.WaitGroup
type waitGroup uint
func (wg *waitGroup) wait() {
n := 0
for *wg != 0 {
// pause and wait to be rescheduled
runtime.Gosched()
if n > 100 {
// if something is using the sleep queue, this may be necessary
time.Sleep(time.Millisecond)
}
n++
}
}
func (wg *waitGroup) add(n uint) {
*wg += waitGroup(n)
}
func (wg *waitGroup) done() {
if *wg == 0 {
panic("wait group underflow")
}
*wg--
}
var wg waitGroup
func main() {
ch := make(chan int)
println("len, cap of channel:", len(ch), cap(ch), ch == nil)
wg.add(1)
go sender(ch)
n, ok := <-ch
println("recv from open channel:", n, ok)
for n := range ch {
if n == 6 {
time.Sleep(time.Microsecond)
}
println("received num:", n)
}
wg.wait()
n, ok = <-ch
println("recv from closed channel:", n, ok)
// Test bigger values
ch2 := make(chan complex128)
wg.add(1)
go sendComplex(ch2)
println("complex128:", <-ch2)
wg.wait()
// Test multi-sender.
ch = make(chan int)
wg.add(3)
go fastsender(ch, 10)
go fastsender(ch, 23)
go fastsender(ch, 40)
slowreceiver(ch)
wg.wait()
// Test multi-receiver.
ch = make(chan int)
wg.add(3)
go fastreceiver(ch)
go fastreceiver(ch)
go fastreceiver(ch)
slowsender(ch)
wg.wait()
// Test iterator style channel.
ch = make(chan int)
wg.add(1)
go iterator(ch, 100)
sum := 0
for i := range ch {
sum += i
}
wg.wait()
println("sum(100):", sum)
// Test simple selects.
go selectDeadlock()
go selectDeadlock() // cannot use waitGroup here - never terminates
wg.add(1)
go selectNoOp()
wg.wait()
// Test select with a single send operation (transformed into chan send).
ch = make(chan int)
wg.add(1)
go fastreceiver(ch)
select {
case ch <- 5:
}
close(ch)
time.Sleep(time.Millisecond)
wg.wait()
println("did send one")
// Test select with a single recv operation (transformed into chan recv).
@@ -70,9 +115,12 @@ func main() {
// Test select recv with channel that has one entry.
ch = make(chan int)
wg.add(1)
go func(ch chan int) {
ch <- 55
wg.done()
}(ch)
// not defined behavior, but we cant really fix this until select has been fixed
time.Sleep(time.Millisecond)
select {
case make(chan int) <- 3:
@@ -82,6 +130,7 @@ func main() {
case n := <-make(chan int):
println("unreachable:", n)
}
wg.wait()
// Test select recv with closed channel.
close(ch)
@@ -96,6 +145,7 @@ func main() {
// Test select send.
ch = make(chan int)
wg.add(1)
go fastreceiver(ch)
time.Sleep(time.Millisecond)
select {
@@ -105,9 +155,49 @@ func main() {
println("unreachable:", n)
}
close(ch)
wg.wait()
// Allow goroutines to exit.
time.Sleep(time.Microsecond)
// test non-concurrent buffered channels
ch = make(chan int, 2)
ch <- 1
ch <- 2
println("non-concurrent channel recieve:", <-ch)
println("non-concurrent channel recieve:", <-ch)
// test closing channels with buffered data
ch <- 3
ch <- 4
close(ch)
println("closed buffered channel recieve:", <-ch)
println("closed buffered channel recieve:", <-ch)
println("closed buffered channel recieve:", <-ch)
// test using buffered channels as regular channels with special properties
wg.add(6)
ch = make(chan int, 2)
go send(ch)
go send(ch)
go send(ch)
go send(ch)
go receive(ch)
go receive(ch)
wg.wait()
close(ch)
var count int
for range ch {
count++
}
println("hybrid buffered channel recieve:", count)
}
func send(ch chan<- int) {
ch <- 1
wg.done()
}
func receive(ch <-chan int) {
<-ch
wg.done()
}
func sender(ch chan int) {
@@ -119,15 +209,18 @@ func sender(ch chan int) {
ch <- i
}
close(ch)
wg.done()
}
func sendComplex(ch chan complex128) {
ch <- 7 + 10.5i
wg.done()
}
func fastsender(ch chan int, n int) {
ch <- n
ch <- n + 1
wg.done()
}
func slowreceiver(ch chan int) {
@@ -153,6 +246,7 @@ func fastreceiver(ch chan int) {
sum += n
}
println("sum:", sum)
wg.done()
}
func iterator(ch chan int, top int) {
@@ -160,6 +254,7 @@ func iterator(ch chan int, top int) {
ch <- i
}
close(ch)
wg.done()
}
func selectDeadlock() {
@@ -174,4 +269,5 @@ func selectNoOp() {
default:
}
println("after no-op")
wg.done()
}
+6
View File
@@ -25,3 +25,9 @@ select n from chan: 55
select n from closed chan: 0
select send
sum: 235
non-concurrent channel recieve: 1
non-concurrent channel recieve: 2
closed buffered channel recieve: 3
closed buffered channel recieve: 4
closed buffered channel recieve: 0
hybrid buffered channel recieve: 2