Skip to content

Commit

Permalink
make a First alias without a context
Browse files Browse the repository at this point in the history
  • Loading branch information
orsinium committed Aug 9, 2023
1 parent 62fabc9 commit 6619419
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 11 deletions.
9 changes: 7 additions & 2 deletions channels/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,12 @@ func Filter[T any](c <-chan T, f func(el T) bool) chan T {
return result
}

// First selects the first available element from the given channels.
// First is an alias for [FirstC] without a context.
func First[T any](cs ...<-chan T) (T, error) {
return FirstC(context.Background(), cs...)
}

// FirstC selects the first available element from the given channels.
//
// The function returns in one of the following cases:
//
Expand Down Expand Up @@ -225,7 +230,7 @@ func Filter[T any](c <-chan T, f func(el T) bool) chan T {
//
// [starvation]: https://en.wikipedia.org/wiki/Starvation_(computer_science)
// [Go specification]: https://go.dev/ref/spec#Select_statements
func First[T any](ctx context.Context, cs ...<-chan T) (T, error) {
func FirstC[T any](ctx context.Context, cs ...<-chan T) (T, error) {
// try to use a regular select if a small number of channels is passed
switch len(cs) {
case 0:
Expand Down
27 changes: 18 additions & 9 deletions channels/channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,15 @@ func TestFilter(t *testing.T) {
}

func TestFirst(t *testing.T) {
is := is.New(t)
c := make(chan int, 2)
c <- 42
v, err := channels.First(c)
is.NoErr(err)
is.Equal(v, 42)
}

func TestFirstC(t *testing.T) {
t.Parallel()
is := is.New(t)
ctx := context.Background()
Expand All @@ -216,7 +225,7 @@ func TestFirst(t *testing.T) {
csR[i] = c
}
go func() { csW[i] <- 12 }()
v, err := channels.First(ctx, csR...)
v, err := channels.FirstC(ctx, csR...)
is.NoErr(err)
is.Equal(v, 12)
}
Expand All @@ -226,42 +235,42 @@ func TestFirst(t *testing.T) {
}
}

_, err := channels.First[int](ctx)
_, err := channels.FirstC[int](ctx)
is.Equal(err, channels.ErrEmpty)
}

func TestFirst_Cancellation(t *testing.T) {
func TestFirstC_Cancellation(t *testing.T) {
t.Parallel()
is := is.New(t)
ctx := context.Background()

// one closed channel
c1 := make(chan int)
close(c1)
_, err := channels.First(ctx, c1)
_, err := channels.FirstC(ctx, c1)
is.Equal(err, channels.ErrClosed)

// two channels, one closed
c2 := make(chan int)
_, err = channels.First(ctx, c2, c1)
_, err = channels.FirstC(ctx, c2, c1)
is.Equal(err, channels.ErrClosed)

// one channel, context cancelled on wait
ctx2, cancel := context.WithCancel(context.Background())
cancel()
_, err = channels.First(ctx2, c2)
_, err = channels.FirstC(ctx2, c2)
is.Equal(err, context.Canceled)

// two channels, context cancelled on wait
c3 := make(chan int)
c4 := make(chan int)
ctx3, cancel := context.WithCancel(context.Background())
cancel()
_, err = channels.First(ctx3, c3, c4)
_, err = channels.FirstC(ctx3, c3, c4)
is.Equal(err, context.Canceled)
}

func TestFirst_Starvation(t *testing.T) {
func TestFirstC_Starvation(t *testing.T) {
t.Parallel()
is := is.New(t)

Expand Down Expand Up @@ -296,7 +305,7 @@ func TestFirst_Starvation(t *testing.T) {
// Calculate how many messages are received from each channel.
nReceived := make(map[int]uint32)
for k := 0; k < nChannels*4; k++ {
i, err := channels.First(context.Background(), csR...)
i, err := channels.FirstC(context.Background(), csR...)
is.NoErr(err)
nReceived[i] += 1
}
Expand Down

0 comments on commit 6619419

Please sign in to comment.