"""Task replacement and execution share a lock; queued old actions are rejected.""" from threading import RLock from uuid import uuid4 from .actions import Ticket class Rejected(ValueError): pass class Authority: def __init__(self): self.lock = RLock() self.session_id = uuid4().hex self.task_id = "" self.epoch = 0 self.sequence = 0 self.goal = "" def replace(self, goal): if not isinstance(goal, str) or not goal.strip(): raise ValueError("task needs a nonempty goal") with self.lock: self.task_id = uuid4().hex self.epoch += 1 self.sequence = 0 self.goal = goal def ticket(self, state): with self.lock: if not self.task_id: raise Rejected("NO_TASK") return Ticket(self.session_id, self.task_id, self.epoch, self.sequence, state.document_id, state.revision, state.state_hash) def validate(self, ticket, state): if ticket != self.ticket(state): raise Rejected("STALE_AUTHORITY_OR_OBSERVATION") def consume(self): self.sequence += 1