| 1 | package jobs |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "time" |
| 6 | ) |
| 7 | |
| 8 | // Close cancels the session context and waits briefly for every background job |
| 9 | // goroutine to return before unblocking. If a non-cooperative job ignores |
| 10 | // cancellation, cleanup of the temporary artifact root continues in the |
| 11 | // background after the goroutines eventually unwind. |
| 12 | func (m *Manager) Close() { |
| 13 | _ = m.CloseWithGrace(m.teardownGrace) |
| 14 | } |
| 15 | |
| 16 | // CloseAsync cancels the manager and returns immediately. It is used when a |
| 17 | // caller has already begun session-specific teardown and owns the delayed |
| 18 | // persistent cleanup, but still needs the manager's root context and temporary |
| 19 | // artifact root released eventually. |
| 20 | func (m *Manager) CloseAsync() { |
| 21 | m.mu.Lock() |
| 22 | m.cancel() |
| 23 | m.mu.Unlock() |
| 24 | go func() { |
| 25 | m.wg.Wait() |
| 26 | m.releaseOwner() |
| 27 | m.removeTempRoot() |
| 28 | }() |
| 29 | } |
| 30 | |
| 31 | // CloseWithGrace is Close with an explicit wait window, used by tests and |
| 32 | // callers that need to surface non-cooperative jobs. |
| 33 | func (m *Manager) CloseWithGrace(grace time.Duration) TeardownResult { |
| 34 | m.mu.Lock() |
| 35 | m.cancel() |
| 36 | m.mu.Unlock() |
| 37 | done := make(chan struct{}) |
| 38 | go func() { |
| 39 | m.wg.Wait() |
| 40 | m.releaseOwner() |
| 41 | close(done) |
| 42 | }() |
| 43 | result, timedOut := waitTeardownTargets(context.Background(), m.closeTargets(), grace, done) |
| 44 | if timedOut { |
| 45 | m.emitTeardownTimeout("close", result) |
| 46 | go func() { |
| 47 | <-done |
| 48 | m.removeTempRoot() |
| 49 | }() |
| 50 | return result |
| 51 | } |
| 52 | m.removeTempRoot() |
| 53 | return result |
| 54 | } |
| 55 |