| 1 | package shutdown |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "fmt" |
| 6 | "time" |
| 7 | |
| 8 | logging "github.com/ipfs/go-log/v2" |
| 9 | ) |
| 10 | |
| 11 | var closeLog = logging.Logger("shutdown") |
| 12 | |
| 13 | // CloseWithCtx runs close in a goroutine and returns when it finishes or |
| 14 | // when ctx is done, whichever comes first. If ctx fires before close |
| 15 | // returns, the goroutine is leaked intentionally; the process is about to |
| 16 | // exit, so the leak is bounded by process lifetime. Logs at ERROR which |
| 17 | // subsystem failed to close in time so operators see it in journal/docker |
| 18 | // logs. |
| 19 | func CloseWithCtx(ctx context.Context, name string, close func() error) error { |
| 20 | done := make(chan error, 1) |
| 21 | start := time.Now() |
| 22 | go func() { done <- close() }() |
| 23 | select { |
| 24 | case err := <-done: |
| 25 | return err |
| 26 | case <-ctx.Done(): |
| 27 | closeLog.Errorf("subsystem %q failed to close within shutdown deadline (after %s): %s", |
| 28 | name, time.Since(start), ctx.Err()) |
| 29 | return fmt.Errorf("%s close: %w", name, ctx.Err()) |
| 30 | } |
| 31 | } |