You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

138 lines
2.8 KiB

package lucifer3
import (
"log"
"sync"
)
type ServiceKey struct{}
type Event interface {
EventName() string
}
type EventBus struct {
mu sync.Mutex
listeners []*serviceListener
signal chan struct{}
}
// JoinCallback joins the event bus for a moment.
func (b *EventBus) JoinCallback(cb func(event Event, sender ServiceID) bool) {
b.Join(newCallbackService(cb))
}
func (b *EventBus) Join(service Service) {
// Take the signal here so that it will receive the events sent by the calling function
// so that they're processed without having to wait for a new event.
signal := b.signalCh()
b.mu.Lock()
listener := &serviceListener{
bus: b,
queue: make([]queuedEvent, 0, 16),
service: service,
}
go listener.run(signal)
b.listeners = append(b.listeners, listener)
b.mu.Unlock()
}
func (b *EventBus) Send(event Event, sender ServiceID, recipient *ServiceID) {
b.mu.Lock()
defer b.mu.Unlock()
deleteList := make([]int, 0, 0)
for i, listener := range b.listeners {
if !listener.service.Active() {
deleteList = append(deleteList, i-len(deleteList))
continue
}
if recipient != nil && *recipient != listener.service.ServiceID() {
continue
}
listener.mu.Lock()
listener.queue = append(listener.queue, queuedEvent{
event: event,
sender: sender,
})
listener.mu.Unlock()
}
for i := range deleteList {
b.listeners = append(b.listeners[:i], b.listeners[i+1:]...)
}
if recipient != nil {
log.Printf("%s (-> %s): %s", &sender, recipient, event.EventName())
} else {
log.Printf("%s: %s", &sender, event.EventName())
}
if b.signal != nil {
close(b.signal)
b.signal = nil
}
}
func (b *EventBus) signalCh() <-chan struct{} {
b.mu.Lock()
defer b.mu.Unlock()
if b.signal == nil {
b.signal = make(chan struct{})
}
return b.signal
}
type serviceListener struct {
mu sync.Mutex
bus *EventBus
queue []queuedEvent
service Service
}
func (l *serviceListener) run(signal <-chan struct{}) {
queue := make([]queuedEvent, 0, 16)
for {
// Listen for the signal, but stop here if the service has marked
// itself as inactive.
<-signal
if !l.service.Active() {
return
}
// Take a new signal before copying the queue to avoid delay
// in case a message is about to be enqueued right now!
signal = l.bus.signalCh()
// Take a copy of the queue.
l.mu.Lock()
for _, message := range l.queue {
queue = append(queue, message)
}
l.queue = l.queue[:0]
l.mu.Unlock()
// Handle the messages in the queue, but stop mid-queue if the previous message
// or external circumstances marked the service inactive.
for _, message := range queue {
if !l.service.Active() {
return
}
l.service.Handle(l.bus, message.event, message.sender)
}
queue = queue[:0]
}
}
type queuedEvent struct {
event Event
sender ServiceID
}