From a77de922988631477a199aca1ed7c4732e649782 Mon Sep 17 00:00:00 2001 From: Eugene R Date: Fri, 28 Jul 2023 13:38:04 +0300 Subject: [PATCH] fix goroutine leak after calling Future.Get() (#8) --- future.go | 4 ++-- future_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/future.go b/future.go index 87c9440..7cfe66d 100644 --- a/future.go +++ b/future.go @@ -50,7 +50,7 @@ var _ Future[any] = (*FutureImpl[any])(nil) // NewFuture returns a new Future. func NewFuture[T any]() Future[T] { return &FutureImpl[T]{ - done: make(chan interface{}), + done: make(chan interface{}, 1), } } @@ -71,7 +71,7 @@ func (fut *FutureImpl[T]) acceptTimeout(timeout time.Duration) { case result := <-fut.done: fut.setResult(result) case <-timer.C: - fut.err = fmt.Errorf("Future timeout after %s", timeout) + fut.setResult(fmt.Errorf("Future timeout after %s", timeout)) } }) } diff --git a/future_test.go b/future_test.go index 591bf8a..5bf3d9f 100644 --- a/future_test.go +++ b/future_test.go @@ -2,6 +2,9 @@ package async import ( "errors" + "fmt" + "runtime" + "sync" "testing" "time" @@ -106,3 +109,36 @@ func TestFutureTimeout(t *testing.T) { _, err = future.Join() internal.AssertErrorContains(t, err, "timeout") } + +func TestFutureGoroutineLeak(t *testing.T) { + var wg sync.WaitGroup + + fmt.Println(runtime.NumGoroutine()) + + numFuture := 100 + for i := 0; i < numFuture; i++ { + promise := NewPromise[string]() + wg.Add(1) + go func() { + defer wg.Done() + time.Sleep(time.Millisecond * 100) + promise.Success("OK") + }() + wg.Add(1) + go func() { + defer wg.Done() + fut := promise.Future() + _, _ = fut.Get(time.Millisecond * 10) + time.Sleep(time.Millisecond * 100) + _, _ = fut.Join() + }() + } + + wg.Wait() + time.Sleep(time.Millisecond * 10) + numGoroutine := runtime.NumGoroutine() + fmt.Println(numGoroutine) + if numGoroutine > numFuture { + t.Fatalf("numGoroutine is %d", numGoroutine) + } +}