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 }