hodu/bulletin.go

137 lines
2.6 KiB
Go
Raw Normal View History

2025-03-08 15:17:27 +09:00
package hodu
import "container/list"
2025-03-10 19:56:14 +09:00
import "errors"
import "sync"
2025-03-10 19:56:14 +09:00
type BulletinSubscription[T interface{}] struct {
C chan T
b *Bulletin[T]
topic string
node *list.Element
2025-03-08 15:17:27 +09:00
}
type BulletinSubscriptionList = *list.List
2025-03-10 19:56:14 +09:00
type BulletinSubscriptionMap map[string]BulletinSubscriptionList
2025-03-10 19:56:14 +09:00
type Bulletin[T interface{}] struct {
sbsc_map BulletinSubscriptionMap
sbsc_mtx sync.RWMutex
2025-03-10 19:56:14 +09:00
closed bool
2025-03-08 15:17:27 +09:00
}
2025-03-10 19:56:14 +09:00
func NewBulletin[T interface{}]() *Bulletin[T] {
return &Bulletin[T]{
sbsc_map: make(BulletinSubscriptionMap, 0),
}
}
2025-03-10 19:56:14 +09:00
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
2025-03-10 19:56:14 +09:00
if b.closed { return nil, errors.New("closed bulletin") }
sbsc.C = make(chan T, 128) // TODO: size?
sbsc.b = b
sbsc.topic = topic
2025-03-10 19:56:14 +09:00
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()
2025-03-10 19:56:14 +09:00
return &sbsc, nil
2025-03-08 15:17:27 +09:00
}
2025-03-10 19:56:14 +09:00
func (b *Bulletin[T]) Unsubscribe(sbsc *BulletinSubscription[T]) {
if sbsc.b == b && sbsc.node != nil {
var sl BulletinSubscriptionList
var ok bool
2025-03-08 15:17:27 +09:00
b.sbsc_mtx.Lock()
sl, ok = b.sbsc_map[sbsc.topic]
2025-03-10 19:56:14 +09:00
if ok {
sl.Remove(sbsc.node)
close(sbsc.C)
sbsc.node = nil
sbsc.b = nil
}
b.sbsc_mtx.Unlock()
}
2025-03-08 15:17:27 +09:00
}
2025-03-10 19:56:14 +09:00
func (b *Bulletin[T]) Publish(topic string, data T) {
var sl BulletinSubscriptionList
var ok bool
2025-03-10 19:56:14 +09:00
var topics [2]string
var t string
if b.closed { return }
if topic == "" { return }
topics[0] = topic
topics[1] = ""
b.sbsc_mtx.Lock()
2025-03-10 19:56:14 +09:00
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()
}