|
- // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Source code and contact info at http://github.com/streadway/amqp
-
- package amqp
-
- import (
- "os"
- "strconv"
- "sync"
- "sync/atomic"
- )
-
- var consumerSeq uint64
-
- const consumerTagLengthMax = 0xFF // see writeShortstr
-
- func uniqueConsumerTag() string {
- return commandNameBasedUniqueConsumerTag(os.Args[0])
- }
-
- func commandNameBasedUniqueConsumerTag(commandName string) string {
- tagPrefix := "ctag-"
- tagInfix := commandName
- tagSuffix := "-" + strconv.FormatUint(atomic.AddUint64(&consumerSeq, 1), 10)
-
- if len(tagPrefix)+len(tagInfix)+len(tagSuffix) > consumerTagLengthMax {
- tagInfix = "streadway/amqp"
- }
-
- return tagPrefix + tagInfix + tagSuffix
- }
-
- type consumerBuffers map[string]chan *Delivery
-
- // Concurrent type that manages the consumerTag ->
- // ingress consumerBuffer mapping
- type consumers struct {
- sync.WaitGroup // one for buffer
- closed chan struct{} // signal buffer
-
- sync.Mutex // protects below
- chans consumerBuffers
- }
-
- func makeConsumers() *consumers {
- return &consumers{
- closed: make(chan struct{}),
- chans: make(consumerBuffers),
- }
- }
-
- func (subs *consumers) buffer(in chan *Delivery, out chan Delivery) {
- defer close(out)
- defer subs.Done()
-
- var inflight = in
- var queue []*Delivery
-
- for delivery := range in {
- queue = append(queue, delivery)
-
- for len(queue) > 0 {
- select {
- case <-subs.closed:
- // closed before drained, drop in-flight
- return
-
- case delivery, consuming := <-inflight:
- if consuming {
- queue = append(queue, delivery)
- } else {
- inflight = nil
- }
-
- case out <- *queue[0]:
- queue = queue[1:]
- }
- }
- }
- }
-
- // On key conflict, close the previous channel.
- func (subs *consumers) add(tag string, consumer chan Delivery) {
- subs.Lock()
- defer subs.Unlock()
-
- if prev, found := subs.chans[tag]; found {
- close(prev)
- }
-
- in := make(chan *Delivery)
- subs.chans[tag] = in
-
- subs.Add(1)
- go subs.buffer(in, consumer)
- }
-
- func (subs *consumers) cancel(tag string) (found bool) {
- subs.Lock()
- defer subs.Unlock()
-
- ch, found := subs.chans[tag]
-
- if found {
- delete(subs.chans, tag)
- close(ch)
- }
-
- return found
- }
-
- func (subs *consumers) close() {
- subs.Lock()
- defer subs.Unlock()
-
- close(subs.closed)
-
- for tag, ch := range subs.chans {
- delete(subs.chans, tag)
- close(ch)
- }
-
- subs.Wait()
- }
-
- // Sends a delivery to a the consumer identified by `tag`.
- // If unbuffered channels are used for Consume this method
- // could block all deliveries until the consumer
- // receives on the other end of the channel.
- func (subs *consumers) send(tag string, msg *Delivery) bool {
- subs.Lock()
- defer subs.Unlock()
-
- buffer, found := subs.chans[tag]
- if found {
- buffer <- msg
- }
-
- return found
- }
|