github.com/nalind/docker@v1.5.0/pkg/pubsub/publisher.go (about)

     1  package pubsub
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  // NewPublisher creates a new pub/sub publisher to broadcast messages.
     9  // The duration is used as the send timeout as to not block the publisher publishing
    10  // messages to other clients if one client is slow or unresponsive.
    11  // The buffer is used when creating new channels for subscribers.
    12  func NewPublisher(publishTimeout time.Duration, buffer int) *Publisher {
    13  	return &Publisher{
    14  		buffer:      buffer,
    15  		timeout:     publishTimeout,
    16  		subscribers: make(map[subscriber]struct{}),
    17  	}
    18  }
    19  
    20  type subscriber chan interface{}
    21  
    22  type Publisher struct {
    23  	m           sync.RWMutex
    24  	buffer      int
    25  	timeout     time.Duration
    26  	subscribers map[subscriber]struct{}
    27  }
    28  
    29  // Len returns the number of subscribers for the publisher
    30  func (p *Publisher) Len() int {
    31  	p.m.RLock()
    32  	i := len(p.subscribers)
    33  	p.m.RUnlock()
    34  	return i
    35  }
    36  
    37  // Subscribe adds a new subscriber to the publisher returning the channel.
    38  func (p *Publisher) Subscribe() chan interface{} {
    39  	ch := make(chan interface{}, p.buffer)
    40  	p.m.Lock()
    41  	p.subscribers[ch] = struct{}{}
    42  	p.m.Unlock()
    43  	return ch
    44  }
    45  
    46  // Evict removes the specified subscriber from receiving any more messages.
    47  func (p *Publisher) Evict(sub chan interface{}) {
    48  	p.m.Lock()
    49  	delete(p.subscribers, sub)
    50  	close(sub)
    51  	p.m.Unlock()
    52  }
    53  
    54  // Publish sends the data in v to all subscribers currently registered with the publisher.
    55  func (p *Publisher) Publish(v interface{}) {
    56  	p.m.RLock()
    57  	for sub := range p.subscribers {
    58  		// send under a select as to not block if the receiver is unavailable
    59  		select {
    60  		case sub <- v:
    61  		case <-time.After(p.timeout):
    62  		}
    63  	}
    64  	p.m.RUnlock()
    65  }
    66  
    67  // Close closes the channels to all subscribers registered with the publisher.
    68  func (p *Publisher) Close() {
    69  	p.m.Lock()
    70  	for sub := range p.subscribers {
    71  		close(sub)
    72  	}
    73  	p.m.Unlock()
    74  }