mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-09-20 11:09:52 +02:00
perf(eventsystem): reduce duration locks are held
also added a test to ensure there is not a deadlock if a listener is marked as subscribed to an event, but not registered
This commit is contained in:
@@ -93,7 +93,7 @@ func (em *eventManager) EmitToSubscribersAnd(listenerIDs []ListenerID, payload e
|
|||||||
eventName := payload.GetName()
|
eventName := payload.GetName()
|
||||||
|
|
||||||
if len(listenerIDs) == 0 {
|
if len(listenerIDs) == 0 {
|
||||||
return // No subscribers
|
return // No listeners specified
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the event
|
// Create the event
|
||||||
@@ -109,8 +109,8 @@ func (em *eventManager) EmitToSubscribersAnd(listenerIDs []ListenerID, payload e
|
|||||||
|
|
||||||
// Also emit to all subscribers of the event as usual
|
// Also emit to all subscribers of the event as usual
|
||||||
em.mutex.RLock()
|
em.mutex.RLock()
|
||||||
|
defer em.mutex.RUnlock()
|
||||||
subscribers, exists := em.subscriptions[eventName]
|
subscribers, exists := em.subscriptions[eventName]
|
||||||
em.mutex.RUnlock()
|
|
||||||
if !exists || len(subscribers) == 0 {
|
if !exists || len(subscribers) == 0 {
|
||||||
return // No subscribers
|
return // No subscribers
|
||||||
}
|
}
|
||||||
@@ -150,14 +150,14 @@ func (em *eventManager) emitTo(listenerIDs []ListenerID, event events.Event) {
|
|||||||
// Dispatch to all specified listeners asynchronously
|
// Dispatch to all specified listeners asynchronously
|
||||||
em.mutex.RLock()
|
em.mutex.RLock()
|
||||||
defer em.mutex.RUnlock()
|
defer em.mutex.RUnlock()
|
||||||
|
listenersToUnregister := []ListenerID{}
|
||||||
for _, listenerID := range listenerIDs {
|
for _, listenerID := range listenerIDs {
|
||||||
listener, exists := em.subscribers[listenerID]
|
listener, exists := em.subscribers[listenerID]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
em.logger.PrintAndLog("event-system", "Failed to get listener for event dispatch, removing "+string(listenerID)+" from subscriptions", nil)
|
em.logger.PrintAndLog("event-system", "Failed to get listener for event dispatch, removing "+string(listenerID)+" from subscriptions", nil)
|
||||||
// Remove the listener from the subscription list
|
// Mark for removal
|
||||||
// This is done in a separate goroutine to avoid deadlock
|
listenersToUnregister = append(listenersToUnregister, listenerID)
|
||||||
go em.UnregisterSubscriber(listenerID)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,4 +167,11 @@ func (em *eventManager) emitTo(listenerIDs []ListenerID, event events.Event) {
|
|||||||
}
|
}
|
||||||
}(listener)
|
}(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unregister any listeners that no longer exist, asynchronously
|
||||||
|
go func() {
|
||||||
|
for _, id := range listenersToUnregister {
|
||||||
|
em.UnregisterSubscriber(id)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
@@ -247,7 +247,7 @@ func TestEventEmissionToSpecificListener(t *testing.T) {
|
|||||||
select {
|
select {
|
||||||
case <-listener1.receivedEvents:
|
case <-listener1.receivedEvents:
|
||||||
t.Fatal("Listener1 should not have received any events")
|
t.Fatal("Listener1 should not have received any events")
|
||||||
case <-time.After(500 * time.Millisecond):
|
case <-time.After(100 * time.Millisecond):
|
||||||
// No event received, as expected
|
// No event received, as expected
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ func TestEventEmissionToSpecificListener(t *testing.T) {
|
|||||||
select {
|
select {
|
||||||
case <-listener2.receivedEvents:
|
case <-listener2.receivedEvents:
|
||||||
t.Fatal("Listener2 should not have received any new events")
|
t.Fatal("Listener2 should not have received any new events")
|
||||||
case <-time.After(500 * time.Millisecond):
|
case <-time.After(100 * time.Millisecond):
|
||||||
// No event received, as expected
|
// No event received, as expected
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ func TestEventEmissionToSpecificListener(t *testing.T) {
|
|||||||
"Hello from pluginA": false,
|
"Hello from pluginA": false,
|
||||||
"Hello from pluginB": false,
|
"Hello from pluginB": false,
|
||||||
}
|
}
|
||||||
for i := 0; i < 2; i++ {
|
for range expectedMessagesSeen {
|
||||||
select {
|
select {
|
||||||
case receivedEvent := <-moderator.receivedEvents:
|
case receivedEvent := <-moderator.receivedEvents:
|
||||||
if receivedEvent.Name != events.EventCustom {
|
if receivedEvent.Name != events.EventCustom {
|
||||||
@@ -319,7 +319,69 @@ func TestEventEmissionToSpecificListener(t *testing.T) {
|
|||||||
select {
|
select {
|
||||||
case <-otherListener.receivedEvents:
|
case <-otherListener.receivedEvents:
|
||||||
t.Fatal("otherListener should not have received any events")
|
t.Fatal("otherListener should not have received any events")
|
||||||
case <-time.After(500 * time.Millisecond):
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
// No event received, as expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmissionToNonExistentListener(t *testing.T) {
|
||||||
|
// Create a test listener and register it
|
||||||
|
listenerID := ListenerID("testListener")
|
||||||
|
testListener := &TestListener{
|
||||||
|
id: listenerID,
|
||||||
|
receivedEvents: make(chan events.Event, 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create event manager with the test listener marked as subscribed to BlacklistToggledEvents,
|
||||||
|
// but not actually registered
|
||||||
|
logger, err := logger.NewFmtLogger()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create logger: %v", err)
|
||||||
|
}
|
||||||
|
em := eventManager{
|
||||||
|
subscriptions: map[events.EventName][]ListenerID{
|
||||||
|
events.EventBlacklistToggled: {listenerID},
|
||||||
|
},
|
||||||
|
subscribers: make(map[ListenerID]Listener),
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit a BlacklistToggled event
|
||||||
|
testEvent := &events.BlacklistToggledEvent{
|
||||||
|
RuleID: "rule123",
|
||||||
|
Enabled: false,
|
||||||
|
}
|
||||||
|
eventEmitted := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
em.Emit(testEvent)
|
||||||
|
time.Sleep(10 * time.Millisecond) // Give some time for the emission to process
|
||||||
|
close(eventEmitted)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for the event emission to complete
|
||||||
|
select {
|
||||||
|
case <-eventEmitted:
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
t.Fatal("Timeout waiting for event emission to complete, likely due to deadlock")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the listener is still tracked by the event manager
|
||||||
|
em.mutex.RLock()
|
||||||
|
_, isRegistered := em.subscribers[listenerID]
|
||||||
|
subscribers := em.subscriptions[events.EventBlacklistToggled]
|
||||||
|
em.mutex.RUnlock()
|
||||||
|
if len(subscribers) != 0 {
|
||||||
|
t.Fatal("Listener should have been removed from subscriptions after failed dispatch")
|
||||||
|
}
|
||||||
|
if isRegistered {
|
||||||
|
t.Fatal("Listener was somehow registered")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the listener was unregistered, it should not receive any events
|
||||||
|
select {
|
||||||
|
case <-testListener.receivedEvents:
|
||||||
|
t.Fatal("Listener should not have received any events after being unregistered")
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
// No event received, as expected
|
// No event received, as expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user