mirror of https://github.com/golang/go.git
cmd/go2go, go/go2go: add chans test case
To make this work, add support for instantiating pointer types. Change-Id: Ifcecf863ad4194014a242d0a0d01ca3dc0288811
This commit is contained in:
parent
8d18d04312
commit
22a2e4f0c2
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package chans provides utility functions for working with channels.
|
||||
package chans
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// ReadAll reads from c until the channel is closed or the context is
|
||||
// canceled, returning all the values read.
|
||||
func ReadAll(type Elem)(ctx context.Context, c <-chan Elem) []Elem {
|
||||
var r []Elem
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return r
|
||||
case v, ok := <-c:
|
||||
if !ok {
|
||||
return r
|
||||
}
|
||||
r = append(r, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges two channels into a single channel.
|
||||
// This will leave a goroutine running until either both channels are closed
|
||||
// or the context is canceled, at which point the returned channel is closed.
|
||||
func Merge(type Elem)(ctx context.Context, c1, c2 <-chan Elem) <-chan Elem {
|
||||
r := make(chan Elem)
|
||||
go func(ctx context.Context, c1, c2 <-chan Elem, r chan<- Elem) {
|
||||
defer close(r)
|
||||
for c1 != nil || c2 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case v1, ok := <-c1:
|
||||
if ok {
|
||||
r <- v1
|
||||
} else {
|
||||
c1 = nil
|
||||
}
|
||||
case v2, ok := <-c2:
|
||||
if ok {
|
||||
r <- v2
|
||||
} else {
|
||||
c2 = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}(ctx, c1, c2, r)
|
||||
return r
|
||||
}
|
||||
|
||||
// Filter calls f on each value read from c. If f returns true the value
|
||||
// is sent on the returned channel. This will leave a goroutine running
|
||||
// until c is closed or the context is canceled, at which point the
|
||||
// returned channel is closed.
|
||||
func Filter(type Elem)(ctx context.Context, c <-chan Elem, f func(Elem) bool) <-chan Elem {
|
||||
r := make(chan Elem)
|
||||
go func(ctx context.Context, c <-chan Elem, f func(Elem) bool, r chan<- Elem) {
|
||||
defer close(r)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case v, ok := <-c:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if f(v) {
|
||||
r <- v
|
||||
}
|
||||
}
|
||||
}
|
||||
}(ctx, c, f, r)
|
||||
return r
|
||||
}
|
||||
|
||||
// Sink returns a channel that discards all values sent to it.
|
||||
// This will leave a goroutine running until the context is canceled
|
||||
// or the returned channel is closed.
|
||||
func Sink(type Elem)(ctx context.Context) chan<- Elem {
|
||||
r := make(chan Elem)
|
||||
go func(ctx context.Context, r <-chan Elem) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case _, ok := <-r:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}(ctx, r)
|
||||
return r
|
||||
}
|
||||
|
||||
// An Exclusive is a value that may only be used by a single goroutine
|
||||
// at a time. This is implemented using channels rather than a mutex.
|
||||
type Exclusive(type Val) struct {
|
||||
c chan Val
|
||||
}
|
||||
|
||||
// MakeExclusive makes an initialized exclusive value.
|
||||
func MakeExclusive(type Val)(initial Val) *Exclusive(Val) {
|
||||
r := &Exclusive(Val){
|
||||
c: make(chan Val, 1),
|
||||
}
|
||||
r.c <- initial
|
||||
return r
|
||||
}
|
||||
|
||||
// Acquire acquires the exclusive value for private use.
|
||||
// It must be released using the Release method.
|
||||
func (e *Exclusive(Val)) Acquire() Val {
|
||||
return <-e.c
|
||||
}
|
||||
|
||||
// TryAcquire attempts to acquire the value. The ok result reports whether
|
||||
// the value was acquired. If the value is acquired, it must be released
|
||||
// using the Release method.
|
||||
func (e *Exclusive(Val)) TryAcquire() (v Val, ok bool) {
|
||||
select {
|
||||
case r := <-e.c:
|
||||
return r, true
|
||||
default:
|
||||
return v, false
|
||||
}
|
||||
}
|
||||
|
||||
// Release updates and releases the value.
|
||||
// This method panics if the value has not been acquired.
|
||||
func (e *Exclusive(Val)) Release(v Val) {
|
||||
select {
|
||||
case e.c <- v:
|
||||
default:
|
||||
panic("Exclusive Release without Acquire")
|
||||
}
|
||||
}
|
||||
|
||||
// Ranger returns a Sender and a Receiver. The Receiver provides a
|
||||
// Next method to retrieve values. The Sender provides a Send method
|
||||
// to send values and a Close method to stop sending values. The Next
|
||||
// method indicates when the Sender has been closed, and the Send
|
||||
// method indicates when the Receiver has been freed.
|
||||
//
|
||||
// This is a convenient way to exit a goroutine sending values when
|
||||
// the receiver stops reading them.
|
||||
func Ranger(type Elem)() (*Sender(Elem), *Receiver(Elem)) {
|
||||
c := make(chan Elem)
|
||||
d := make(chan struct{})
|
||||
s := &Sender(Elem){
|
||||
values: c,
|
||||
done: d,
|
||||
}
|
||||
r := &Receiver(Elem) {
|
||||
values: c,
|
||||
done: d,
|
||||
}
|
||||
runtime.SetFinalizer(r, (*Receiver(Elem)).finalize)
|
||||
return s, r
|
||||
}
|
||||
|
||||
// A Sender is used to send values to a Receiver.
|
||||
type Sender(type Elem) struct {
|
||||
values chan<- Elem
|
||||
done <-chan struct{}
|
||||
}
|
||||
|
||||
// Send sends a value to the receiver. It reports whether the value was sent.
|
||||
// The value will not be sent if the context is closed or the receiver
|
||||
// is freed.
|
||||
func (s *Sender(Elem)) Send(ctx context.Context, v Elem) bool {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case s.values <- v:
|
||||
return true
|
||||
case <-s.done:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Close tells the receiver that no more values will arrive.
|
||||
// After Close is called, the Sender may no longer be used.
|
||||
func (s *Sender(Elem)) Close() {
|
||||
close(s.values)
|
||||
}
|
||||
|
||||
// A Receiver receives values from a Sender.
|
||||
type Receiver(type Elem) struct {
|
||||
values <-chan Elem
|
||||
done chan<- struct{}
|
||||
}
|
||||
|
||||
// Next returns the next value from the channel. The bool result indicates
|
||||
// whether the value is valid.
|
||||
func (r *Receiver(Elem)) Next(ctx context.Context) (v Elem, ok bool) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case v, ok = <-r.values:
|
||||
}
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// finalize is a finalizer for the receiver.
|
||||
func (r *Receiver(Elem)) finalize() {
|
||||
close(r.done)
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package chans
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"slices"
|
||||
)
|
||||
|
||||
func TestReadAll(t *testing.T) {
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
c <- 4
|
||||
c <- 2
|
||||
c <- 5
|
||||
close(c)
|
||||
}()
|
||||
// FIXME: Shouldn't need to specify type here.
|
||||
got := ReadAll(int)(context.Background(), c)
|
||||
want := []int{4, 2, 5}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("ReadAll returned %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
c1 := make(chan int)
|
||||
c2 := make(chan int)
|
||||
go func() {
|
||||
c1 <- 1
|
||||
c1 <- 3
|
||||
c1 <- 5
|
||||
close(c1)
|
||||
}()
|
||||
go func() {
|
||||
c2 <- 2
|
||||
c2 <- 4
|
||||
c2 <- 6
|
||||
close(c2)
|
||||
}()
|
||||
ctx := context.Background()
|
||||
// FIXME: Shouldn't need to specify type here.
|
||||
got := ReadAll(ctx, Merge(int)(ctx, c1, c2))
|
||||
sort.Ints(got)
|
||||
want := []int{1, 2, 3, 4, 5, 6}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("Merge returned %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
c <- 1
|
||||
c <- 2
|
||||
c <- 3
|
||||
close(c)
|
||||
}()
|
||||
even := func(i int) bool { return i%2 == 0 }
|
||||
ctx := context.Background()
|
||||
// FIXME: Shouldn't need to specify type here.
|
||||
got := ReadAll(ctx, Filter(int)(ctx, c, even))
|
||||
want := []int{2}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("Filter returned %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSink(t *testing.T) {
|
||||
c := Sink(int)(context.Background())
|
||||
after := time.NewTimer(time.Minute)
|
||||
defer after.Stop()
|
||||
send := func(v int) {
|
||||
select {
|
||||
case c <- v:
|
||||
case <-after.C:
|
||||
t.Error("timed out sending to Sink")
|
||||
}
|
||||
}
|
||||
send(1)
|
||||
send(2)
|
||||
send(3)
|
||||
close(c)
|
||||
}
|
||||
|
||||
func TestExclusive(t *testing.T) {
|
||||
val := 0
|
||||
ex := MakeExclusive(&val)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
f := func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < 10; i++ {
|
||||
p := ex.Acquire()
|
||||
(*p)++
|
||||
ex.Release(p)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Add(2)
|
||||
go f()
|
||||
go f()
|
||||
|
||||
wg.Wait()
|
||||
if val != 20 {
|
||||
t.Errorf("after Acquire/Release loop got %d, want 20", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExclusiveTry(t *testing.T) {
|
||||
s := ""
|
||||
ex := MakeExclusive(&s)
|
||||
p, ok := ex.TryAcquire()
|
||||
if !ok {
|
||||
t.Fatalf("TryAcquire failed")
|
||||
}
|
||||
*p = "a"
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, ok := ex.TryAcquire()
|
||||
if ok {
|
||||
t.Errorf("TryAcquire succeeded unexpectedly")
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
ex.Release(p)
|
||||
|
||||
p, ok = ex.TryAcquire()
|
||||
if !ok {
|
||||
t.Errorf("TryAcquire failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRanger(t *testing.T) {
|
||||
s, r := Ranger(int)()
|
||||
|
||||
ctx := context.Background()
|
||||
go func() {
|
||||
// Receive one value then exit.
|
||||
v, ok := r.Next(ctx)
|
||||
if !ok {
|
||||
t.Errorf("did not receive any values")
|
||||
} else if v != 1 {
|
||||
t.Errorf("received %d, want 1", v)
|
||||
}
|
||||
}()
|
||||
|
||||
c1 := make(chan bool)
|
||||
c2 := make(chan bool)
|
||||
go func() {
|
||||
defer close(c2)
|
||||
if !s.Send(ctx, 1) {
|
||||
t.Errorf("Send failed unexpectedly")
|
||||
}
|
||||
close(c1)
|
||||
if s.Send(ctx, 2) {
|
||||
t.Errorf("Send succeeded unexpectedly")
|
||||
}
|
||||
}()
|
||||
|
||||
<-c1
|
||||
|
||||
// Force a garbage collection to try to get the finalizers to run.
|
||||
runtime.GC()
|
||||
|
||||
select {
|
||||
case <-c2:
|
||||
case <-time.After(time.Minute):
|
||||
t.Error("Ranger Send should have failed, but timed out")
|
||||
}
|
||||
}
|
||||
|
|
@ -80,6 +80,13 @@ func (t *translator) doInstantiateType(ta *typeArgs, typ types.Type) types.Type
|
|||
return typ
|
||||
}
|
||||
return types.NewSlice(instElem)
|
||||
case *types.Pointer:
|
||||
elem := typ.Elem()
|
||||
instElem := t.instantiateType(ta, elem)
|
||||
if elem == instElem {
|
||||
return typ
|
||||
}
|
||||
return types.NewPointer(instElem)
|
||||
case *types.Signature:
|
||||
params := t.instantiateTypeTuple(ta, typ.Params())
|
||||
results := t.instantiateTypeTuple(ta, typ.Results())
|
||||
|
|
|
|||
Loading…
Reference in New Issue