セマフォのctx引数ミスで永遠に待ってしまう例

Go のコードレビューしてて次のようなコードに遭遇した。sem.Acquire() にわたす ctx が context.Background() になってしまっていて、WithTimeline が切れても ctx.Done にいってくれない、ということがあった。

解決策としては、 sem.Acquire() に同じ ctx を渡すだけで解決。

こういうの、わかってしまえばそれだけかってなるけど、機械的に検出することができないのが悩ましいところ。

問題のあったコード

package main

import (
    "context"
    "golang.org/x/sync/semaphore"
    "log"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    sem := semaphore.NewWeighted(3)

    for {
        select {
        case <-ctx.Done():
            log.Fatal("timeout.")
            return
        default:
          if err := sem.Acquire(context.Background(), 1); err != nil {
              log.Fatal(err)
            }
          log.Printf("sem acquired!")
          go (func() {
              defer sem.Release(1)
              time.Sleep(10*time.Second)
              log.Printf("go func done!")
            })()
        }
    }
}

解決策

-        if err := sem.Acquire(context.Background(), 1); err != nil {
+        if err := sem.Acquire(ctx, 1); err != nil {