github.com/sagernet/sing@v0.4.0-beta.19.0.20240518125136-f67a0988a636/common/observable/observer.go (about) 1 package observable 2 3 import ( 4 "os" 5 "sync" 6 ) 7 8 type Observable[T any] interface { 9 Subscribe() (subscription Subscription[T], done <-chan struct{}, err error) 10 UnSubscribe(subscription Subscription[T]) 11 } 12 13 type Observer[T any] struct { 14 subscriber *Subscriber[T] 15 listenerSize int 16 listener map[Subscription[T]]*Subscriber[T] 17 mux sync.Mutex 18 done bool 19 } 20 21 func NewObserver[T any](subscriber *Subscriber[T], listenerBufferSize int) *Observer[T] { 22 observable := &Observer[T]{ 23 subscriber: subscriber, 24 listener: map[Subscription[T]]*Subscriber[T]{}, 25 listenerSize: listenerBufferSize, 26 } 27 go observable.process() 28 return observable 29 } 30 31 func (o *Observer[T]) process() { 32 subscription, done := o.subscriber.Subscription() 33 process: 34 for { 35 select { 36 case <-done: 37 break process 38 case entry := <-subscription: 39 o.mux.Lock() 40 for _, sub := range o.listener { 41 sub.Emit(entry) 42 } 43 o.mux.Unlock() 44 } 45 } 46 o.mux.Lock() 47 defer o.mux.Unlock() 48 for _, listener := range o.listener { 49 listener.Close() 50 } 51 } 52 53 func (o *Observer[T]) Subscribe() (subscription Subscription[T], done <-chan struct{}, err error) { 54 o.mux.Lock() 55 defer o.mux.Unlock() 56 if o.done { 57 return nil, nil, os.ErrClosed 58 } 59 subscriber := NewSubscriber[T](o.listenerSize) 60 subscription, done = subscriber.Subscription() 61 o.listener[subscription] = subscriber 62 return 63 } 64 65 func (o *Observer[T]) UnSubscribe(subscription Subscription[T]) { 66 o.mux.Lock() 67 defer o.mux.Unlock() 68 subscriber, exist := o.listener[subscription] 69 if !exist { 70 return 71 } 72 delete(o.listener, subscription) 73 subscriber.Close() 74 } 75 76 func (o *Observer[T]) Emit(item T) { 77 o.subscriber.Emit(item) 78 } 79 80 func (o *Observer[T]) Close() error { 81 o.mux.Lock() 82 defer o.mux.Unlock() 83 if o.done { 84 return os.ErrClosed 85 } 86 o.subscriber.Close() 87 o.done = true 88 return nil 89 }