Hacker News new | past | comments | ask | show | jobs | submit login

The function you write after time.After should use the same context, and check its Done channel before continuing execution



Probably best not to use `time.After`, because it indeed starts a timer that you have no control over unless you are waiting for the full time.


Good point. To those unaware, the time.After is equivalent to time.NewTimer(d).C, but "the underlying Timer is not recovered by the garbage collector until the timer fires" (quote from the doc).

That slowAPICall function should look like:

  func slowAPICall(ctx context.Context) string {
     d := rand.Intn(5)
     t := time.NewTimer(time.Duration(d) * time.Second)
     defer t.Stop()
     ...
  }


So you have to propagate the Context, hm. IIRC, go test will panic the test case when it times out. Not exactly sure tho. It would be nice if there was a kind of 'abort' feature to clean up subroutines spun off this thread


In most cases, at the inner-most level you end up calling some sort of external library (sql, api-client, ...) that will handle the Done() channel itself.

All you have to do is make sure is to pass to the library the context that carries your timeout or cancellation signal. The "rule" that everyone seems to follow is to always take as first argument a context.Context if your library handles cancellation.


The best there is with the context package is to make sure to call the cancel function given to you by contexts that have cancelation. Usually you do this via defer. The cancel function is a no-op if the context is finished otherwise. All this ends up doing though is making sure that things that clean themselves up know to clean themselves up eventually.


> Usually you do this via defer.

I agree this is usually done by defer, but you probably should not do it that way unless your code is very simple. Consider a function body which I've seen variants of many times:

    ctx, cancel = context.WithTimeout(pctx, timeout)
    defer cancel()
    if resp, err := do(ctx, req); err == nil {
        process(resp)
    } 
    return err
Safe yes, but optimal? process doesn't use the context, and may take longer than the timeout. The context will continue running, with some associated resource cost (at the very least, the context's goroutine and timer). A minimal change is:

    ctx, cancel = context.WithTimeout(pctx, timeout)
    resp, err := do(ctx, req)
    cancel()
    if err == nil {
        process(resp)
    } 
    return err
Which disposes of those resources much earlier.

(Depending on your Go compiler version there is also a potential cost associated simply with using defer; this is independent of that.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: