Sobes.tech
Middle+

func Do(ctx context.Context, users []User) (map[string]int64, error) { var mu sync.Mutex var wg sync.WaitGroup ch := make(chan error, len(users)) names := make(map[string]int64, 0) ctxWithCancel, cancel := context.WithCancel(ctx) wg.Add(len(users)) for _, u := range users { go func() { defer wg.Done() select { case name, err := fetch(ctx, u): if err != nil { ch <- err return } mu.Lock() names[name] = names[name] + 1 mu.Unlock() case <-ctxWithCancel.Err: } }() } return names, nil }

sobes.tech AI

Answer from AI

The provided code has several issues:

  1. Inside the for _, u := range users loop, a goroutine is launched, but the variable u is not passed into the closure, which can lead to data races and incorrect behavior.

  2. In the select statement, case name, err := fetch(ctx, u): is used, but this syntax is invalid in selectselect works with channels, not functions.

  3. The error channel ch is filled, but never read from, which can cause blocking.

  4. The context ctxWithCancel is created, but cancel() is not called, which can lead to leaks.

  5. The function returns names and nil immediately, without waiting for goroutines to finish.

Corrected example:

func Do(ctx context.Context, users []User) (map[string]int64, error) {
    var mu sync.Mutex
    var wg sync.WaitGroup
    ch := make(chan error, len(users))
    names := make(map[string]int64)
    ctxWithCancel, cancel := context.WithCancel(ctx)
    defer cancel()

    wg.Add(len(users))
    for _, u := range users {
        u := u // local copy for closure
        go func() {
            defer wg.Done()
            name, err := fetch(ctxWithCancel, u)
            if err != nil {
                ch <- err
                cancel() // cancel remaining operations
                return
            }
            mu.Lock()
            names[name]++
            mu.Unlock()
        }()
    }

    wg.Wait()
    close(ch)

    for err := range ch {
        if err != nil {
            return nil, err
        }
    }

    return names, nil
}

Main changes:

  • Created a local copy u for each goroutine.
  • Removed select with function call; fetch is called directly.
  • Added cancel() call upon error.
  • Added waiting for all goroutines to finish with wg.Wait().
  • Errors are collected from the channel and returned.

This ensures correct parallel processing and proper management of context and errors.