github.com/rentongzhang/docker@v1.8.2-rc1/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  // Publisher is basic pub/sub structure. Allows to send events and subscribe
    23  // to them. Can be safely used from multiple goroutines.
    24  type Publisher struct {
    25  	m           sync.RWMutex
    26  	buffer      int
    27  	timeout     time.Duration
    28  	subscribers map[subscriber]struct{}
    29  }
    30  
    31  // Len returns the number of subscribers for the publisher
    32  func (p *Publisher) Len() int {
    33  	p.m.RLock()
    34  	i := len(p.subscribers)
    35  	p.m.RUnlock()
    36  	return i
    37  }
    38  
    39  // Subscribe adds a new subscriber to the publisher returning the channel.
    40  func (p *Publisher) Subscribe() chan interface{} {
    41  	ch := make(chan interface{}, p.buffer)
    42  	p.m.Lock()
    43  	p.subscribers[ch] = struct{}{}
    44  	p.m.Unlock()
    45  	return ch
    46  }
    47  
    48  // Evict removes the specified subscriber from receiving any more messages.
    49  func (p *Publisher) Evict(sub chan interface{}) {
    50  	p.m.Lock()
    51  	delete(p.subscribers, sub)
    52  	close(sub)
    53  	p.m.Unlock()
    54  }
    55  
    56  // Publish sends the data in v to all subscribers currently registered with the publisher.
    57  func (p *Publisher) Publish(v interface{}) {
    58  	p.m.RLock()
    59  	for sub := range p.subscribers {
    60  		// send under a select as to not block if the receiver is unavailable
    61  		if p.timeout > 0 {
    62  			select {
    63  			case sub <- v:
    64  			case <-time.After(p.timeout):
    65  			}
    66  			continue
    67  		}
    68  		select {
    69  		case sub <- v:
    70  		default:
    71  		}
    72  	}
    73  	p.m.RUnlock()
    74  }
    75  
    76  // Close closes the channels to all subscribers registered with the publisher.
    77  func (p *Publisher) Close() {
    78  	p.m.Lock()
    79  	for sub := range p.subscribers {
    80  		delete(p.subscribers, sub)
    81  		close(sub)
    82  	}
    83  	p.m.Unlock()
    84  }