Download this file
| 1 | package release |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "os" |
| 6 | "sync/atomic" |
| 7 | "time" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/rootstate" |
| 10 | ) |
| 11 | |
| 12 | type ReleaseExclusion struct { |
| 13 | Open func() (rootstate.Store, error) |
| 14 | Wait time.Duration |
| 15 | } |
| 16 | |
| 17 | type releaseHeldKey struct{} |
| 18 | type releaseHeld struct { |
| 19 | owner *ReleaseExclusion |
| 20 | active atomic.Bool |
| 21 | } |
| 22 | |
| 23 | func (x *ReleaseExclusion) Check(ctx context.Context) error { |
| 24 | held, _ := ctx.Value(releaseHeldKey{}).(*releaseHeld) |
| 25 | if x == nil || held == nil || held.owner != x || !held.active.Load() { |
| 26 | return os.ErrPermission |
| 27 | } |
| 28 | return ctx.Err() |
| 29 | } |
| 30 | |
| 31 | func (x *ReleaseExclusion) With(ctx context.Context, fn func(context.Context) error) error { |
| 32 | if x == nil || x.Open == nil || fn == nil { |
| 33 | return os.ErrPermission |
| 34 | } |
| 35 | if ctx.Value(releaseHeldKey{}) != nil { |
| 36 | if err := x.Check(ctx); err != nil { |
| 37 | return err |
| 38 | } |
| 39 | return fn(ctx) |
| 40 | } |
| 41 | store, err := x.Open() |
| 42 | if err != nil { |
| 43 | return err |
| 44 | } |
| 45 | if store == nil { |
| 46 | return os.ErrPermission |
| 47 | } |
| 48 | defer store.Close() |
| 49 | wait := ctx |
| 50 | cancel := func() {} |
| 51 | if x.Wait > 0 { |
| 52 | wait, cancel = context.WithTimeout(ctx, x.Wait) |
| 53 | } |
| 54 | defer cancel() |
| 55 | return rootstate.WithRelease(wait, store, func() error { |
| 56 | cancel() |
| 57 | held := &releaseHeld{owner: x} |
| 58 | held.active.Store(true) |
| 59 | defer held.active.Store(false) |
| 60 | return fn(context.WithValue(ctx, releaseHeldKey{}, held)) |
| 61 | }) |
| 62 | } |
| 63 | |