traefik/vendor/github.com/BurntSushi/ty/fun/chan.go
2017-03-09 13:13:02 +01:00

84 lines
1.9 KiB
Go

package fun
import (
"reflect"
"github.com/BurntSushi/ty"
)
// AsyncChan has a parametric type:
//
// func AsyncChan(chan A) (send chan<- A, recv <-chan A)
//
// AsyncChan provides a channel abstraction without a fixed size buffer.
// The input should be a pointer to a channel that has a type without a
// direction, e.g., `new(chan int)`. Two new channels are returned: `send` and
// `recv`. The caller must send data on the `send` channel and receive data on
// the `recv` channel.
//
// Implementation is inspired by Kyle Lemons' work:
// https://github.com/kylelemons/iq/blob/master/iq_slice.go
func AsyncChan(baseChan interface{}) (send, recv interface{}) {
chk := ty.Check(
new(func(*chan ty.A) (chan ty.A, chan ty.A)),
baseChan)
// We don't care about the baseChan---it is only used to construct
// the return types.
tsend, trecv := chk.Returns[0], chk.Returns[1]
buf := make([]reflect.Value, 0, 10)
rsend := reflect.MakeChan(tsend, 0)
rrecv := reflect.MakeChan(trecv, 0)
go func() {
defer rrecv.Close()
BUFLOOP:
for {
if len(buf) == 0 {
rv, ok := rsend.Recv()
if !ok {
break BUFLOOP
}
buf = append(buf, rv)
}
cases := []reflect.SelectCase{
// case v, ok := <-send
{
Dir: reflect.SelectRecv,
Chan: rsend,
},
// case recv <- buf[0]
{
Dir: reflect.SelectSend,
Chan: rrecv,
Send: buf[0],
},
}
choice, rval, rok := reflect.Select(cases)
switch choice {
case 0:
// case v, ok := <-send
if !rok {
break BUFLOOP
}
buf = append(buf, rval)
case 1:
// case recv <- buf[0]
buf = buf[1:]
default:
panic("bug")
}
}
for _, rv := range buf {
rrecv.Send(rv)
}
}()
// Create the directional channel types.
tsDir := reflect.ChanOf(reflect.SendDir, tsend.Elem())
trDir := reflect.ChanOf(reflect.RecvDir, trecv.Elem())
return rsend.Convert(tsDir).Interface(), rrecv.Convert(trDir).Interface()
}