Run runs the given function in a new background context. Panics thrown in the function are logged and sent to sentry. The routines context is canceled if the program receives a shutdown signal (SIGINT, SIGTERM), if the returned CancelFunc is called, or if the routine returned.
(ctx context.Context, routine func(context.Context))
| 86 | // canceled if the program receives a shutdown signal (SIGINT, SIGTERM), if the |
| 87 | // returned CancelFunc is called, or if the routine returned. |
| 88 | func Run(ctx context.Context, routine func(context.Context)) (cancel context.CancelFunc) { |
| 89 | ctx = context.WithoutCancel(ctx) |
| 90 | |
| 91 | // add routine number to context and logger |
| 92 | num := atomic.AddInt64(&ctr, 1) |
| 93 | |
| 94 | ctx = context.WithValue(ctx, ctxNumKey{}, num) |
| 95 | logger := log.Ctx(ctx).With().Int64("routine", num).Logger() |
| 96 | ctx = logger.WithContext(ctx) |
| 97 | // get cancel function |
| 98 | ctx, cancel = context.WithCancel(ctx) |
| 99 | |
| 100 | // register context to be cancelled when the program is shut down |
| 101 | contextsMx.Lock() |
| 102 | contexts[num] = cancel |
| 103 | contextsMx.Unlock() |
| 104 | // deregister the above if context is done |
| 105 | go func() { |
| 106 | <-ctx.Done() |
| 107 | // In case of a shutdown, this will block forever. But it doesn't hurt, |
| 108 | // because the program is about to exit anyway. |
| 109 | contextsMx.Lock() |
| 110 | defer contextsMx.Unlock() |
| 111 | delete(contexts, num) |
| 112 | }() |
| 113 | go func() { |
| 114 | defer errors.HandleWithCtx(ctx, fmt.Sprintf("routine %d", num)) // handle panics |
| 115 | defer cancel() |
| 116 | |
| 117 | span := sentry.StartSpan(ctx, "function", sentry.WithDescription(fmt.Sprintf("routine %d", num))) |
| 118 | defer span.Finish() |
| 119 | |
| 120 | routine(span.Context()) |
| 121 | }() |
| 122 | return |
| 123 | } |
| 124 | |
| 125 | type ctxNumKey struct{} |
| 126 |