From 37befc7b29e58c4f0526e59d68c4afa74a173d91 Mon Sep 17 00:00:00 2001 From: Vladislav Byrgazov Date: Tue, 13 Jan 2026 20:43:25 +0500 Subject: [PATCH] homework: implement err group with context propagation Signed-off-by: Vladislav Byrgazov --- homeworks/15_contexts/README.md | 18 +++++++++ homeworks/15_contexts/homework.go | 42 +++++++++++++++++++ homeworks/15_contexts/homework_test.go | 56 ++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 homeworks/15_contexts/README.md create mode 100644 homeworks/15_contexts/homework.go create mode 100644 homeworks/15_contexts/homework_test.go diff --git a/homeworks/15_contexts/README.md b/homeworks/15_contexts/README.md new file mode 100644 index 0000000..bdb6794 --- /dev/null +++ b/homeworks/15_contexts/README.md @@ -0,0 +1,18 @@ +# # Домашнее задание №15 + +**В домашнем задании нужно реализовать err group.** + +**Err group** - это паттерн/подход в языке программирования Go, представляющий собой группу горутин, которые работают вместе для достижения общей цели. Он обеспечивает простой и эффективный способ отслеживания ошибок, возникающих в любое время. + +API для err group будет выглядеть следующим образом: + +```go +type Group struct { ... } + +func NewErrGroup(ctx context.Context) +func (g *Group) Go(action func() error) +func (g *Group) Wait() error +``` + +Для выполнения домашнего задания подготовлен шаблон кода и основные тесты, которую помогут проверить корректность реализации конвертации. Шаблона доступен по [ссылке](https://github.com/Balun-courses/deep_go/blob/master/homework/contexts/homework_test.go). + diff --git a/homeworks/15_contexts/homework.go b/homeworks/15_contexts/homework.go new file mode 100644 index 0000000..1fcfff3 --- /dev/null +++ b/homeworks/15_contexts/homework.go @@ -0,0 +1,42 @@ +package homework15 + +import ( + "context" + "sync" +) + +type Group struct { + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + once sync.Once + err error +} + +func NewErrGroup(ctx context.Context) (*Group, context.Context) { + contex, cancel := context.WithCancel(ctx) + return &Group{ + ctx: contex, + cancel: cancel, + }, contex +} + +func (g *Group) Go(action func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + if err := action(); err != nil { + g.once.Do(func() { + g.cancel() + g.err = err + }) + } + + }() +} + +func (g *Group) Wait() error { + g.wg.Wait() + return g.err +} diff --git a/homeworks/15_contexts/homework_test.go b/homeworks/15_contexts/homework_test.go new file mode 100644 index 0000000..47bc25f --- /dev/null +++ b/homeworks/15_contexts/homework_test.go @@ -0,0 +1,56 @@ +package homework15 + +import ( + "context" + "errors" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestErrGroupWithoutError(t *testing.T) { + var counter atomic.Int32 + group, _ := NewErrGroup(context.Background()) + + for i := 0; i < 5; i++ { + group.Go(func() error { + time.Sleep(time.Second) + counter.Add(1) + return nil + }) + } + + err := group.Wait() + assert.Equal(t, int32(5), counter.Load()) + assert.NoError(t, err) +} + +func TestErrGroupWithError(t *testing.T) { + var counter atomic.Int32 + group, ctx := NewErrGroup(context.Background()) + + for i := 0; i < 5; i++ { + group.Go(func() error { + timer := time.NewTimer(time.Second) + defer timer.Stop() + + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + counter.Add(1) + return nil + } + }) + } + + group.Go(func() error { + return errors.New("error") + }) + + err := group.Wait() + assert.Equal(t, int32(0), counter.Load()) + assert.Error(t, err) +}