|
|
|
@ -5,13 +5,13 @@ import (
|
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"v2ray.com/core/common" |
|
|
|
|
"v2ray.com/core/common/signal/done" |
|
|
|
|
"v2ray.com/core/common/task" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type Subscriber struct { |
|
|
|
|
name string |
|
|
|
|
buffer chan interface{} |
|
|
|
|
removed chan struct{} |
|
|
|
|
buffer chan interface{} |
|
|
|
|
done *done.Instance |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Subscriber) push(msg interface{}) { |
|
|
|
@ -25,62 +25,66 @@ func (s *Subscriber) Wait() <-chan interface{} {
|
|
|
|
|
return s.buffer |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Subscriber) Close() { |
|
|
|
|
close(s.removed) |
|
|
|
|
func (s *Subscriber) Close() error { |
|
|
|
|
return s.done.Close() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Subscriber) IsClosed() bool { |
|
|
|
|
select { |
|
|
|
|
case <-s.removed: |
|
|
|
|
return true |
|
|
|
|
default: |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
return s.done.Done() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type Service struct { |
|
|
|
|
sync.RWMutex |
|
|
|
|
subs []*Subscriber |
|
|
|
|
subs map[string][]*Subscriber |
|
|
|
|
ctask *task.Periodic |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func NewService() *Service { |
|
|
|
|
s := &Service{} |
|
|
|
|
s := &Service{ |
|
|
|
|
subs: make(map[string][]*Subscriber), |
|
|
|
|
} |
|
|
|
|
s.ctask = &task.Periodic{ |
|
|
|
|
Execute: s.cleanup, |
|
|
|
|
Execute: s.Cleanup, |
|
|
|
|
Interval: time.Second * 30, |
|
|
|
|
} |
|
|
|
|
common.Must(s.ctask.Start()) |
|
|
|
|
return s |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) cleanup() error { |
|
|
|
|
// Cleanup cleans up internal caches of subscribers.
|
|
|
|
|
// Visible for testing only.
|
|
|
|
|
func (s *Service) Cleanup() error { |
|
|
|
|
s.Lock() |
|
|
|
|
defer s.Unlock() |
|
|
|
|
|
|
|
|
|
if len(s.subs) < 16 { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
newSub := make([]*Subscriber, 0, len(s.subs)) |
|
|
|
|
for _, sub := range s.subs { |
|
|
|
|
if !sub.IsClosed() { |
|
|
|
|
newSub = append(newSub, sub) |
|
|
|
|
for name, subs := range s.subs { |
|
|
|
|
newSub := make([]*Subscriber, 0, len(s.subs)) |
|
|
|
|
for _, sub := range subs { |
|
|
|
|
if !sub.IsClosed() { |
|
|
|
|
newSub = append(newSub, sub) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if len(newSub) == 0 { |
|
|
|
|
delete(s.subs, name) |
|
|
|
|
} else { |
|
|
|
|
s.subs[name] = newSub |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
s.subs = newSub |
|
|
|
|
if len(s.subs) == 0 { |
|
|
|
|
s.subs = make(map[string][]*Subscriber) |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) Subscribe(name string) *Subscriber { |
|
|
|
|
sub := &Subscriber{ |
|
|
|
|
name: name, |
|
|
|
|
buffer: make(chan interface{}, 16), |
|
|
|
|
removed: make(chan struct{}), |
|
|
|
|
buffer: make(chan interface{}, 16), |
|
|
|
|
done: done.New(), |
|
|
|
|
} |
|
|
|
|
s.Lock() |
|
|
|
|
s.subs = append(s.subs, sub) |
|
|
|
|
subs := append(s.subs[name], sub) |
|
|
|
|
s.subs[name] = subs |
|
|
|
|
s.Unlock() |
|
|
|
|
return sub |
|
|
|
|
} |
|
|
|
@ -89,8 +93,8 @@ func (s *Service) Publish(name string, message interface{}) {
|
|
|
|
|
s.RLock() |
|
|
|
|
defer s.RUnlock() |
|
|
|
|
|
|
|
|
|
for _, sub := range s.subs { |
|
|
|
|
if sub.name == name && !sub.IsClosed() { |
|
|
|
|
for _, sub := range s.subs[name] { |
|
|
|
|
if !sub.IsClosed() { |
|
|
|
|
sub.push(message) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|