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
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
|
|
}
|