core: Add utils/publisher
This commit is contained in:
		
							parent
							
								
									06294898f6
								
							
						
					
					
						commit
						eee2dd703d
					
				| 
						 | 
					@ -0,0 +1,96 @@
 | 
				
			||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Publisher struct {
 | 
				
			||||||
 | 
						subscribeChannel   chan chan interface{}
 | 
				
			||||||
 | 
						unsubscribeChannel chan (<-chan interface{})
 | 
				
			||||||
 | 
						broadcastChannel   chan interface{}
 | 
				
			||||||
 | 
						closeChannel       chan struct{}
 | 
				
			||||||
 | 
						closedChannel      chan struct{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Subscribable interface {
 | 
				
			||||||
 | 
						Subscribe(queueSize int) <-chan interface{}
 | 
				
			||||||
 | 
						Unsubscribe(channel <-chan interface{})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CreatePublisher() *Publisher {
 | 
				
			||||||
 | 
						re := &Publisher{
 | 
				
			||||||
 | 
							subscribeChannel:   make(chan chan interface{}),
 | 
				
			||||||
 | 
							unsubscribeChannel: make(chan (<-chan interface{})),
 | 
				
			||||||
 | 
							broadcastChannel:   make(chan interface{}),
 | 
				
			||||||
 | 
							closeChannel:       make(chan struct{}),
 | 
				
			||||||
 | 
							closedChannel:      make(chan struct{}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						go re.broadcast()
 | 
				
			||||||
 | 
						return re
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pub *Publisher) Subscribe(queueSize int) <-chan interface{} {
 | 
				
			||||||
 | 
						channel := make(chan interface{}, queueSize)
 | 
				
			||||||
 | 
						pub.subscribeChannel <- channel
 | 
				
			||||||
 | 
						// Read empty value to block until subscribed
 | 
				
			||||||
 | 
						<-channel
 | 
				
			||||||
 | 
						return channel
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pub *Publisher) Unsubscribe(channel <-chan interface{}) {
 | 
				
			||||||
 | 
						pub.unsubscribeChannel <- channel
 | 
				
			||||||
 | 
						// Wait for channel close
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							_, ok := <-channel
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pub *Publisher) Publish(value interface{}) {
 | 
				
			||||||
 | 
						pub.broadcastChannel <- value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pub *Publisher) Close() {
 | 
				
			||||||
 | 
						pub.closeChannel <- struct{}{}
 | 
				
			||||||
 | 
						<-pub.closedChannel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Close channels, so that future use of the object will panic
 | 
				
			||||||
 | 
						close(pub.subscribeChannel)
 | 
				
			||||||
 | 
						close(pub.unsubscribeChannel)
 | 
				
			||||||
 | 
						close(pub.broadcastChannel)
 | 
				
			||||||
 | 
						close(pub.closeChannel)
 | 
				
			||||||
 | 
						close(pub.closedChannel)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pub *Publisher) broadcast() {
 | 
				
			||||||
 | 
						var channels []chan interface{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case channel := <-pub.subscribeChannel:
 | 
				
			||||||
 | 
								channels = append(channels, channel)
 | 
				
			||||||
 | 
								channel <- struct{}{}
 | 
				
			||||||
 | 
							case channel := <-pub.unsubscribeChannel:
 | 
				
			||||||
 | 
								for i, c := range channels {
 | 
				
			||||||
 | 
									if c != channel {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									channels = append(channels[:i], channels[i+1:]...)
 | 
				
			||||||
 | 
									close(c)
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case value := <-pub.broadcastChannel:
 | 
				
			||||||
 | 
								for _, c := range channels {
 | 
				
			||||||
 | 
									select {
 | 
				
			||||||
 | 
									case c <- value:
 | 
				
			||||||
 | 
									default:
 | 
				
			||||||
 | 
										go pub.Unsubscribe(c)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case <-pub.closeChannel:
 | 
				
			||||||
 | 
								for _, c := range channels {
 | 
				
			||||||
 | 
									close(c)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								pub.closedChannel <- struct{}{}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue