goroutine泄漏与上下文超时?

In the code below, a client is putting a string on a service's input channel and listening for a reply on either an output or an error channel.

The context is set with a 5ms timeout.

func (s service) run() {
    <-s.input

    go func() {
        select {
        case <-s.ctx.Done():
            s.errs <- errors.New("ctx done")
            return
        }
    }()

    time.Sleep(10 * time.Millisecond)
    s.output <- 42
    fmt.Println("run exit")
}

The code times out correctly (due to the 10ms sleep) and outputs

error:  ctx done

However, the "run exit" is never printed.

Question: Is there a goroutine leak with processes stuck on

s.output <- 42

Go Playground example

Context has timeout of 5 milliseconds and you sleep for 10 millisecond before this line s.output <-42 is run. So the context is timeout first and error occurs, that correct, but take a look at main function:

select {
    case o := <-s.output:
        fmt.Println("output: ", o)
    case err := <-s.errs:
        fmt.Println("error: ", err)
    }

The select statement has reached case err := <-s.errs so it will break the select and go to the end of main function => program exit Even if s.output <- 42 is call simultaneously with s.errs <- errors.New("ctx done") only one case has reached in select statement, if you need to reach second case, put a loop around select statement