enhanced the bulletin code
This commit is contained in:
120
bulletin.go
120
bulletin.go
@ -1,84 +1,136 @@
|
||||
package hodu
|
||||
|
||||
import "container/list"
|
||||
import "errors"
|
||||
import "sync"
|
||||
|
||||
type BulletinChan = chan interface{}
|
||||
|
||||
type BulletinSubscription struct {
|
||||
c chan interface{}
|
||||
b *Bulletin
|
||||
type BulletinSubscription[T interface{}] struct {
|
||||
C chan T
|
||||
b *Bulletin[T]
|
||||
topic string
|
||||
node *list.Element
|
||||
}
|
||||
|
||||
type BulletinSubscriptionList = *list.List
|
||||
|
||||
type BulletinSubscriptionMap map[string]BulletinSubscriptionList
|
||||
|
||||
type Bulletin struct {
|
||||
type Bulletin[T interface{}] struct {
|
||||
sbsc_map BulletinSubscriptionMap
|
||||
sbsc_mtx sync.RWMutex
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewBulletin() *Bulletin {
|
||||
return &Bulletin{
|
||||
sbsc_map: make(BulletinSubscriptionMap, 0),
|
||||
func NewBulletin[T interface{}]() *Bulletin[T] {
|
||||
return &Bulletin[T]{
|
||||
sbsc_map: make(BulletinSubscriptionMap, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bulletin) Subscribe(topic string) *BulletinSubscription {
|
||||
var sbsc BulletinSubscription
|
||||
func (b *Bulletin[T]) unsubscribe_all_nolock() {
|
||||
var topic string
|
||||
var sl BulletinSubscriptionList
|
||||
|
||||
for topic, sl = range b.sbsc_map {
|
||||
var sbsc *BulletinSubscription[T]
|
||||
var e *list.Element
|
||||
|
||||
for e = sl.Front(); e != nil; e = e.Next() {
|
||||
sbsc = e.Value.(*BulletinSubscription[T])
|
||||
close(sbsc.C)
|
||||
sbsc.b = nil
|
||||
sbsc.node = nil
|
||||
}
|
||||
|
||||
delete(b.sbsc_map, topic)
|
||||
}
|
||||
|
||||
b.closed = true
|
||||
}
|
||||
|
||||
func (b *Bulletin[T]) UnsubscribeAll() {
|
||||
b.sbsc_mtx.Lock()
|
||||
b.unsubscribe_all_nolock()
|
||||
b.sbsc_mtx.Unlock()
|
||||
}
|
||||
|
||||
func (b *Bulletin[T]) Close() {
|
||||
b.sbsc_mtx.Lock()
|
||||
if !b.closed {
|
||||
b.unsubscribe_all_nolock()
|
||||
b.closed = true
|
||||
}
|
||||
b.sbsc_mtx.Unlock()
|
||||
}
|
||||
|
||||
func (b *Bulletin[T]) Subscribe(topic string) (*BulletinSubscription[T], error) {
|
||||
var sbsc BulletinSubscription[T]
|
||||
var sbsc_list BulletinSubscriptionList
|
||||
var ok bool
|
||||
|
||||
sbsc.b = b
|
||||
sbsc.c = make(chan interface{})
|
||||
sbsc.topic = topic
|
||||
b.sbsc_mtx.Lock()
|
||||
if b.closed { return nil, errors.New("closed bulletin") }
|
||||
|
||||
sbsc.C = make(chan T, 128) // TODO: size?
|
||||
sbsc.b = b
|
||||
sbsc.topic = topic
|
||||
|
||||
b.sbsc_mtx.Lock()
|
||||
sbsc_list, ok = b.sbsc_map[topic]
|
||||
if !ok {
|
||||
sbsc_list = list.New()
|
||||
b.sbsc_map[topic] = sbsc_list
|
||||
}
|
||||
|
||||
|
||||
sbsc.node = sbsc_list.PushBack(&sbsc)
|
||||
b.sbsc_mtx.Unlock()
|
||||
return &sbsc
|
||||
return &sbsc, nil
|
||||
}
|
||||
|
||||
func (b *Bulletin) Unsbsccribe(sbsc *BulletinSubscription) {
|
||||
if sbsc.b == b {
|
||||
func (b *Bulletin[T]) Unsubscribe(sbsc *BulletinSubscription[T]) {
|
||||
if sbsc.b == b && sbsc.node != nil {
|
||||
var sl BulletinSubscriptionList
|
||||
var ok bool
|
||||
|
||||
b.sbsc_mtx.Lock()
|
||||
sl, ok = b.sbsc_map[sbsc.topic]
|
||||
if ok { sl.Remove(sbsc.node) }
|
||||
if ok {
|
||||
sl.Remove(sbsc.node)
|
||||
close(sbsc.C)
|
||||
sbsc.node = nil
|
||||
sbsc.b = nil
|
||||
}
|
||||
b.sbsc_mtx.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bulletin) Publish(topic string, data interface{}) {
|
||||
func (b *Bulletin[T]) Publish(topic string, data T) {
|
||||
var sl BulletinSubscriptionList
|
||||
var ok bool
|
||||
var topics [2]string
|
||||
var t string
|
||||
|
||||
if b.closed { return }
|
||||
if topic == "" { return }
|
||||
|
||||
topics[0] = topic
|
||||
topics[1] = ""
|
||||
|
||||
b.sbsc_mtx.Lock()
|
||||
sl, ok = b.sbsc_map[topic]
|
||||
if ok {
|
||||
var sbsc *BulletinSubscription
|
||||
var e *list.Element
|
||||
for e = sl.Front(); e != nil; e = e.Next() {
|
||||
sbsc = e.Value.(*BulletinSubscription)
|
||||
sbsc.c <- data
|
||||
for _, t = range topics {
|
||||
sl, ok = b.sbsc_map[t]
|
||||
if ok {
|
||||
var sbsc *BulletinSubscription[T]
|
||||
var e *list.Element
|
||||
for e = sl.Front(); e != nil; e = e.Next() {
|
||||
sbsc = e.Value.(*BulletinSubscription[T])
|
||||
select {
|
||||
case sbsc.C <- data:
|
||||
// ok. could be written.
|
||||
default:
|
||||
// channel full. discard it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
b.sbsc_mtx.Unlock()
|
||||
}
|
||||
|
||||
func (s *BulletinSubscription) Receive() interface{} {
|
||||
var x interface{}
|
||||
x = <- s.c
|
||||
return x
|
||||
}
|
||||
|
Reference in New Issue
Block a user