package rootparticipation import ( "context" "sync/atomic" ) type transactionKey struct{} type heldTransaction struct { ledger Ledger active atomic.Bool } func TransactionLedger(ctx context.Context) (Ledger, bool, error) { held, ok := ctx.Value(transactionKey{}).(*heldTransaction) if !ok { return Ledger{}, false, nil } if !held.active.Load() { return Ledger{}, true, ErrUnavailable } if err := ctx.Err(); err != nil { return Ledger{}, true, err } return held.ledger, true, nil } func (l Ledger) lockTransaction(ctx context.Context) (func(), error) { held, ok, err := TransactionLedger(ctx) if err != nil { return nil, err } if ok { if held.State != l.State { return nil, ErrUnavailable } return func() {}, nil } return l.State.Lock(ctx, "transaction.lock") } func (l Ledger) WithTransaction(ctx context.Context, fn func(context.Context) error) error { if l.State == nil || fn == nil { return ErrUnavailable } unlock, err := l.lockTransaction(ctx) if err != nil { return err } defer unlock() held := &heldTransaction{ledger: l} held.active.Store(true) defer held.active.Store(false) return fn(context.WithValue(ctx, transactionKey{}, held)) }