carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostbox
2
3import (
4 "context"
5 "errors"
6 "sync"
7
8 "github.com/carlosframework/platform/internal/lease"
9 "github.com/carlosframework/platform/internal/park"
10)
11
12type Renewable interface {
13 Err() error
14 Lost() <-chan struct{}
15 Release(context.Context) error
16}
17type RenewingLeases struct {
18 Region string
19 Open func(context.Context, string, string, string) (Renewable, error)
20}
21
22func (l RenewingLeases) Acquire(ctx context.Context, bucket, scope string) (park.HeldLease, error) {
23 if ctx == nil || l.Region == "" || bucket == "" || scope == "" {
24 return nil, ErrUnsupported
25 }
26 open := l.Open
27 if open == nil {
28 open = func(ctx context.Context, b, p, r string) (Renewable, error) { return lease.Acquire(ctx, b, p, r) }
29 }
30 bounded, cancel := context.WithTimeout(ctx, RequestLimit)
31 h, err := open(bounded, bucket, scope, l.Region)
32 cancel()
33 if err != nil {
34 return nil, err
35 }
36 if h == nil {
37 return nil, ErrUnsupported
38 }
39 cctx, stop := context.WithCancel(ctx)
40 held := &renewingHeld{bucket: bucket, scope: scope, h: h, ctx: cctx, cancel: stop, done: make(chan struct{})}
41 go func() {
42 defer close(held.done)
43 select {
44 case <-h.Lost():
45 stop()
46 case <-cctx.Done():
47 }
48 }()
49 if err := held.Check(ctx); err != nil {
50 held.Close()
51 return nil, err
52 }
53 return held, nil
54}
55
56type renewingHeld struct {
57 bucket, scope string
58 h Renewable
59 ctx context.Context
60 cancel context.CancelFunc
61 done chan struct{}
62 once sync.Once
63 releaseErr error
64}
65
66func (h *renewingHeld) Check(ctx context.Context) error {
67 if err := ctx.Err(); err != nil {
68 return err
69 }
70 if err := h.ctx.Err(); err != nil {
71 return err
72 }
73 return h.h.Err()
74}
75func (h *renewingHeld) Context(ctx context.Context) context.Context {
76 c, cancel := context.WithCancel(ctx)
77 stop := context.AfterFunc(h.ctx, cancel)
78 context.AfterFunc(c, func() { stop() })
79 return c
80}
81func (h *renewingHeld) Release(ctx context.Context) error {
82 h.once.Do(func() {
83 h.cancel()
84 <-h.done
85 bounded, cancel := context.WithTimeout(context.WithoutCancel(ctx), RequestLimit)
86 defer cancel()
87 h.releaseErr = h.h.Release(bounded)
88 })
89 return h.releaseErr
90}
91func (h *renewingHeld) Close() { _ = h.Release(context.Background()) }
92
93// Context carries every constituent's cancellation into the engine's requests.
94func (h *aggregateHeld) Context(ctx context.Context) context.Context {
95 for _, l := range h.leases {
96 if bound, ok := l.(interface {
97 Context(context.Context) context.Context
98 }); ok {
99 ctx = bound.Context(ctx)
100 }
101 }
102 return ctx
103}
104
105// LeasedCheck is useful before requests that are issued by a wake's producer.
106func LeasedCheck(ctx context.Context, health func() error, authority func(context.Context) error) error {
107 if health == nil || authority == nil {
108 return ErrUnsupported
109 }
110 return errors.Join(ctx.Err(), health(), authority(ctx))
111}
112
113func (h *renewingHeld) LeaseScope() (string, string) { return h.bucket, h.scope }
114