@@ -10,6 +10,7 @@ import (
1010 "slices"
1111 "sync"
1212 "sync/atomic"
13+ "time"
1314
1415 "go.uber.org/zap"
1516
@@ -28,6 +29,25 @@ type Cleanup struct {
2829 mu sync.Mutex
2930}
3031
32+ const lateCleanupLogMessage = "cleanup callback ran after cleanup completed"
33+
34+ func runLateCleanup (ctx context.Context , f func (context.Context ) error , priority bool ) {
35+ runCtx := context .WithoutCancel (ctx )
36+ start := time .Now ()
37+ err := f (runCtx )
38+ fields := []zap.Field {
39+ zap .Bool ("priority" , priority ),
40+ zap .Duration ("duration" , time .Since (start )),
41+ }
42+ if err != nil {
43+ logger .L ().Error (runCtx , lateCleanupLogMessage , append (fields , zap .Error (err ))... )
44+
45+ return
46+ }
47+
48+ logger .L ().Info (runCtx , lateCleanupLogMessage , fields ... )
49+ }
50+
3151func NewCleanup () * Cleanup {
3252 return & Cleanup {}
3353}
@@ -37,35 +57,32 @@ func (c *Cleanup) AddNoContext(ctx context.Context, f func() error) {
3757}
3858
3959func (c * Cleanup ) Add (ctx context.Context , f func (ctx context.Context ) error ) {
40- if c .hasRun .Load () == true {
41- err := f (context .WithoutCancel (ctx ))
42- if err != nil {
43- logger .L ().Error (ctx , "failed to run function after cleanup has run" , zap .Error (err ))
44- }
60+ // Keep the state check and registration under the same lock as Run. Without
61+ // this, Add can observe false, lose the lock to Run, then append after Run has
62+ // already drained the list.
63+ c .mu .Lock ()
64+ if ! c .hasRun .Load () {
65+ c .cleanup = append (c .cleanup , f )
66+ c .mu .Unlock ()
4567
4668 return
4769 }
70+ c .mu .Unlock ()
4871
49- c .mu .Lock ()
50- defer c .mu .Unlock ()
51-
52- c .cleanup = append (c .cleanup , f )
72+ runLateCleanup (ctx , f , false )
5373}
5474
5575func (c * Cleanup ) AddPriority (ctx context.Context , f func (ctx context.Context ) error ) {
56- if c .hasRun .Load () == true {
57- err := f (context .WithoutCancel (ctx ))
58- if err != nil {
59- logger .L ().Error (ctx , "failed to run priority function after cleanup has run" , zap .Error (err ))
60- }
76+ c .mu .Lock ()
77+ if ! c .hasRun .Load () {
78+ c .priorityCleanup = append (c .priorityCleanup , f )
79+ c .mu .Unlock ()
6180
6281 return
6382 }
83+ c .mu .Unlock ()
6484
65- c .mu .Lock ()
66- defer c .mu .Unlock ()
67-
68- c .priorityCleanup = append (c .priorityCleanup , f )
85+ runLateCleanup (ctx , f , true )
6986}
7087
7188func (c * Cleanup ) Run (ctx context.Context ) error {
@@ -80,19 +97,21 @@ func (c *Cleanup) run(ctx context.Context) {
8097 c .hasRun .Store (true )
8198
8299 c .mu .Lock ()
83- defer c .mu .Unlock ()
100+ priorityCleanup := c .priorityCleanup
101+ normalCleanup := c .cleanup
102+ c .mu .Unlock ()
84103
85104 var errs []error
86105
87- for _ , cleanup := range slices .Backward (c . priorityCleanup ) {
88- err := cleanup (ctx )
106+ for _ , f := range slices .Backward (priorityCleanup ) {
107+ err := f (ctx )
89108 if err != nil {
90109 errs = append (errs , err )
91110 }
92111 }
93112
94- for _ , cleanup := range slices .Backward (c . cleanup ) {
95- err := cleanup (ctx )
113+ for _ , f := range slices .Backward (normalCleanup ) {
114+ err := f (ctx )
96115 if err != nil {
97116 errs = append (errs , err )
98117 }
0 commit comments