Skip to content

Commit 81019fc

Browse files
committed
fix: align CloseContext with upstream semantics
1 parent 56551c8 commit 81019fc

File tree

3 files changed

+32
-310
lines changed

3 files changed

+32
-310
lines changed

docs/guides/lifecycle.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@ func main() {
156156
}
157157
```
158158

159-
`CloseContext` will return `ctx.Err()` if the context is canceled, but it does not interrupt
160-
closing providers once shutdown begins. Context timeouts are reported as errors; they do not
161-
bound the close duration. Use `App.Close()` if you don't need context-aware errors.
159+
`CloseContext` checks `ctx.Err()` before starting and before each provider close. If the context
160+
is canceled, it returns `ctx.Err()` and leaves the app eligible for a later `Close()` retry.
161+
Use `App.Close()` if you don't need context-aware cancellation behavior.
162162

163163
### Alternative 1: Cleanup in main()
164164

modkit/kernel/bootstrap.go

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"context"
66
"errors"
77
"io"
8-
"sync"
8+
"sync/atomic"
99

1010
"github.com/go-modkit/modkit/modkit/module"
1111
)
@@ -16,8 +16,8 @@ type App struct {
1616
Graph *Graph
1717
container *Container
1818
Controllers map[string]any
19-
closeOnce sync.Once
20-
closeErr error
19+
closed atomic.Bool
20+
closing atomic.Bool
2121
}
2222

2323
func controllerKey(moduleName, controllerName string) string {
@@ -98,20 +98,32 @@ func (a *App) Close() error {
9898

9999
// CloseContext closes providers implementing io.Closer in reverse build order.
100100
func (a *App) CloseContext(ctx context.Context) error {
101-
a.closeOnce.Do(func() {
102-
closers := a.container.closersLIFO()
103-
var errs []error
104-
for _, closer := range closers {
105-
if err := closer.Close(); err != nil {
106-
errs = append(errs, err)
107-
}
108-
}
109-
if ctx != nil && ctx.Err() != nil {
110-
errs = append(errs, ctx.Err())
101+
if a.closed.Load() {
102+
return nil
103+
}
104+
105+
if err := ctx.Err(); err != nil {
106+
return err
107+
}
108+
109+
if !a.closing.CompareAndSwap(false, true) {
110+
return nil
111+
}
112+
defer a.closing.Store(false)
113+
114+
var errs []error
115+
for _, closer := range a.container.closersLIFO() {
116+
if err := ctx.Err(); err != nil {
117+
return err
111118
}
112-
if len(errs) > 0 {
113-
a.closeErr = errors.Join(errs...)
119+
if err := closer.Close(); err != nil {
120+
errs = append(errs, err)
114121
}
115-
})
116-
return a.closeErr
122+
}
123+
if len(errs) == 0 {
124+
a.closed.Store(true)
125+
return nil
126+
}
127+
a.closed.Store(true)
128+
return errors.Join(errs...)
117129
}

modkit/kernel/close_test.go

Lines changed: 0 additions & 290 deletions
This file was deleted.

0 commit comments

Comments
 (0)