package amqp import "sync" // Confirms resequences and notifies one or multiple publisher confirmation listeners type Confirms struct { m sync.Mutex listeners []chan Confirmation sequencer map[uint64]Confirmation published uint64 expecting uint64 } // newConfirms allocates a confirms func newConfirms() *Confirms { return &Confirms{ sequencer: map[uint64]Confirmation{}, published: 0, expecting: 1, } } // Published returns sequential number of published messages func (c *Confirms) Published() uint64 { c.m.Lock() defer c.m.Unlock() return c.published } // Listen is used to listen on incoming confirmations // of publishes func (c *Confirms) Listen(l chan Confirmation) { c.m.Lock() defer c.m.Unlock() c.listeners = append(c.listeners, l) } // Publish increments the publishing counter func (c *Confirms) Publish() uint64 { c.m.Lock() defer c.m.Unlock() c.published++ return c.published } // confirm confirms one publishing, increments the expecting delivery tag, and // removes bookkeeping for that delivery tag. func (c *Confirms) confirm(confirmation Confirmation) { delete(c.sequencer, c.expecting) c.expecting++ for _, l := range c.listeners { l <- confirmation } } // resequence confirms any out of order delivered confirmations func (c *Confirms) resequence() { for c.expecting <= c.published { sequenced, found := c.sequencer[c.expecting] if !found { return } c.confirm(sequenced) } } // One confirms one publishing and all following in the publishing sequence func (c *Confirms) One(confirmed Confirmation) { c.m.Lock() defer c.m.Unlock() if c.expecting == confirmed.DeliveryTag { c.confirm(confirmed) } else { c.sequencer[confirmed.DeliveryTag] = confirmed } c.resequence() } // Multiple confirms all publishings up until the delivery tag func (c *Confirms) Multiple(confirmed Confirmation) { c.m.Lock() defer c.m.Unlock() for c.expecting <= confirmed.DeliveryTag { c.confirm(Confirmation{c.expecting, confirmed.Ack}) } c.resequence() } // Close closes all listeners, discarding any out of sequence confirmations func (c *Confirms) Close() error { c.m.Lock() defer c.m.Unlock() for _, l := range c.listeners { close(l) } c.listeners = nil return nil }