// Package opstate provides a mutex-guarded registry for long-running // operations (e.g. async VM create, async image build). A registry stores // operations by ID and can prune completed ones after a retention window. package opstate import ( "sync" "time" ) // AsyncOp is the protocol each operation type must satisfy. Implementations // own their own concurrency for the returned values — the registry treats // them as opaque. type AsyncOp interface { ID() string IsDone() bool UpdatedAt() time.Time Cancel() } // Registry is a mutex-guarded map of in-flight operations keyed by op ID. // One registry per operation kind; each owns its own lock. type Registry[T AsyncOp] struct { mu sync.Mutex byID map[string]T } // Insert adds op keyed by its ID. func (r *Registry[T]) Insert(op T) { r.mu.Lock() defer r.mu.Unlock() if r.byID == nil { r.byID = map[string]T{} } r.byID[op.ID()] = op } // Get returns the operation with the given ID, if present. func (r *Registry[T]) Get(id string) (T, bool) { r.mu.Lock() defer r.mu.Unlock() op, ok := r.byID[id] return op, ok } // Prune drops completed operations last updated before the cutoff. func (r *Registry[T]) Prune(before time.Time) { r.mu.Lock() defer r.mu.Unlock() for id, op := range r.byID { if !op.IsDone() { continue } if op.UpdatedAt().Before(before) { delete(r.byID, id) } } }