carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package activator
2
3import (
4 "context"
5 "sync"
6 "time"
7)
8
9type cleanupBudgetKey struct{}
10type cleanupContextKey struct{}
11type cleanupBudget struct {
12 mu sync.Mutex
13 base context.Context
14 source context.Context
15 parent *cleanupBudget
16 limit time.Duration
17 ctx context.Context
18 cancel context.CancelFunc
19}
20
21func (b *cleanupBudget) start() context.Context {
22 b.mu.Lock()
23 defer b.mu.Unlock()
24 if b.ctx == nil {
25 base := b.base
26 if b.parent != nil && b.parent.source.Err() != nil {
27 base = b.parent.start()
28 }
29 b.ctx, b.cancel = context.WithTimeout(base, b.limit)
30 }
31 return b.ctx
32}
33
34// WithCleanupBudget shares the cancellation grace period across a request,
35// its nested failure paths, and the owner's final join. Normal work is untimed.
36func WithCleanupBudget(ctx context.Context, limit time.Duration) (context.Context, context.CancelFunc) {
37 parent, _ := ctx.Value(cleanupBudgetKey{}).(*cleanupBudget)
38 b := &cleanupBudget{base: context.WithoutCancel(ctx), source: ctx, parent: parent, limit: limit}
39 stop := context.AfterFunc(ctx, func() { b.start() })
40 return context.WithValue(ctx, cleanupBudgetKey{}, b), func() { stop(); b.start(); b.cancel() }
41}
42
43// CleanupContext keeps an existing cleanup deadline. Cancellation starts the
44// owner's single grace period; it never grants each nested call a fresh one.
45func CleanupContext(ctx context.Context, limit time.Duration) (context.Context, context.CancelFunc) {
46 if ctx.Value(cleanupContextKey{}) != nil {
47 return context.WithTimeout(ctx, limit)
48 }
49 base := context.WithoutCancel(ctx)
50 var stop func() bool
51 if b, ok := ctx.Value(cleanupBudgetKey{}).(*cleanupBudget); ok {
52 if ctx.Err() != nil {
53 base = b.start()
54 }
55 cleanup, cancel := context.WithTimeout(base, limit)
56 stop = context.AfterFunc(ctx, func() { budget := b.start(); context.AfterFunc(budget, cancel) })
57 return context.WithValue(cleanup, cleanupContextKey{}, true), func() { stop(); cancel() }
58 }
59 // Callers without an owner budget still cannot extend an explicit deadline.
60 if deadline, ok := ctx.Deadline(); ok {
61 bounded, cancel := context.WithDeadline(base, deadline)
62 cleanup, done := context.WithTimeout(bounded, limit)
63 return context.WithValue(cleanup, cleanupContextKey{}, true), func() { done(); cancel() }
64 }
65 cleanup, cancel := context.WithTimeout(base, limit)
66 return context.WithValue(cleanup, cleanupContextKey{}, true), cancel
67}
68