99 "reflect"
1010 "strings"
1111 "sync"
12+ "time"
1213)
1314
1415// Event represents an event contract
@@ -21,11 +22,32 @@ type Event interface {
2122// Dispatcher represents an event dispatcher.
2223type Dispatcher struct {
2324 subs sync.Map
25+ done chan struct {} // Cancellation
26+ df time.Duration // Flush interval
2427}
2528
2629// NewDispatcher creates a new dispatcher of events.
2730func NewDispatcher () * Dispatcher {
28- return & Dispatcher {}
31+ return & Dispatcher {
32+ df : 500 * time .Microsecond ,
33+ done : make (chan struct {}),
34+ }
35+ }
36+
37+ // Close closes the dispatcher
38+ func (d * Dispatcher ) Close () error {
39+ close (d .done )
40+ return nil
41+ }
42+
43+ // isClosed returns whether the dispatcher is closed or not
44+ func (d * Dispatcher ) isClosed () bool {
45+ select {
46+ case <- d .done :
47+ return true
48+ default :
49+ return false
50+ }
2951}
3052
3153// Subscribe subscribes to an event, the type of the event will be automatically
@@ -37,21 +59,25 @@ func Subscribe[T Event](broker *Dispatcher, handler func(T)) context.CancelFunc
3759
3860// SubscribeTo subscribes to an event with the specified event type.
3961func SubscribeTo [T Event ](broker * Dispatcher , eventType uint32 , handler func (T )) context.CancelFunc {
40- ctx , cancel := context .WithCancel (context .Background ())
41- sub := & consumer [T ]{
42- queue : make (chan T , 1024 ),
43- exec : handler ,
62+ if broker .isClosed () {
63+ panic (errClosed )
4464 }
4565
4666 // Add to consumer group, if it doesn't exist it will create one
47- s , _ := broker .subs .LoadOrStore (eventType , new (group [T ]))
67+ s , loaded := broker .subs .LoadOrStore (eventType , & group [T ]{
68+ cond : sync .NewCond (new (sync.Mutex )),
69+ })
4870 group := groupOf [T ](eventType , s )
49- group .Add (ctx , sub )
71+ sub := group .Add (handler )
72+
73+ // Start flushing asynchronously if we just created a new group
74+ if ! loaded {
75+ go group .Process (broker .df , broker .done )
76+ }
5077
5178 // Return unsubscribe function
5279 return func () {
5380 group .Del (sub )
54- cancel () // Stop async processing
5581 }
5682}
5783
@@ -80,57 +106,97 @@ func groupOf[T Event](eventType uint32, subs any) *group[T] {
80106 panic (errConflict [T ](eventType , subs ))
81107}
82108
83- // ------------------------------------- Subscriber List -------------------------------------
109+ // ------------------------------------- Subscriber -------------------------------------
84110
85111// consumer represents a consumer with a message queue
86112type consumer [T Event ] struct {
87- queue chan T // Message buffer
88- exec func ( T ) // Process callback
113+ queue [] T // Current work queue
114+ stop bool // Stop signal
89115}
90116
91117// Listen listens to the event queue and processes events
92- func (s * consumer [T ]) Listen (ctx context.Context ) {
118+ func (s * consumer [T ]) Listen (c * sync.Cond , fn func (T )) {
119+ pending := make ([]T , 0 , 128 )
120+
93121 for {
94- select {
95- case ev := <- s .queue :
96- s .exec (ev )
97- case <- ctx .Done ():
98- return
122+ c .L .Lock ()
123+ for len (s .queue ) == 0 {
124+ switch {
125+ case s .stop :
126+ c .L .Unlock ()
127+ return
128+ default :
129+ c .Wait ()
130+ }
131+ }
132+
133+ // Swap buffers and reset the current queue
134+ temp := s .queue
135+ s .queue = pending
136+ pending = temp
137+ s .queue = s .queue [:0 ]
138+ c .L .Unlock ()
139+
140+ // Outside of the critical section, process the work
141+ for i := 0 ; i < len (pending ); i ++ {
142+ fn (pending [i ])
99143 }
100144 }
101145}
102146
147+ // ------------------------------------- Subscriber Group -------------------------------------
148+
103149// group represents a consumer group
104150type group [T Event ] struct {
105- sync.RWMutex
151+ cond * sync.Cond
106152 subs []* consumer [T ]
107153}
108154
155+ // Process periodically broadcasts events
156+ func (s * group [T ]) Process (interval time.Duration , done chan struct {}) {
157+ ticker := time .NewTicker (interval )
158+ for {
159+ select {
160+ case <- done :
161+ return
162+ case <- ticker .C :
163+ s .cond .Broadcast ()
164+ }
165+ }
166+ }
167+
109168// Broadcast sends an event to all consumers
110169func (s * group [T ]) Broadcast (ev T ) {
111- s .RLock ()
112- defer s .RUnlock ()
170+ s .cond .L .Lock ()
113171 for _ , sub := range s .subs {
114- sub .queue <- ev
172+ sub .queue = append ( sub . queue , ev )
115173 }
174+ s .cond .L .Unlock ()
116175}
117176
118177// Add adds a subscriber to the list
119- func (s * group [T ]) Add (ctx context.Context , sub * consumer [T ]) {
120- go sub .Listen (ctx )
178+ func (s * group [T ]) Add (handler func (T )) * consumer [T ] {
179+ sub := & consumer [T ]{
180+ queue : make ([]T , 0 , 128 ),
181+ }
121182
122183 // Add the consumer to the list of active consumers
123- s .Lock ()
184+ s .cond . L . Lock ()
124185 s .subs = append (s .subs , sub )
125- s .Unlock ()
186+ s .cond .L .Unlock ()
187+
188+ // Start listening
189+ go sub .Listen (s .cond , handler )
190+ return sub
126191}
127192
128193// Del removes a subscriber from the list
129194func (s * group [T ]) Del (sub * consumer [T ]) {
130- s .Lock ()
131- defer s .Unlock ()
195+ s .cond . L . Lock ()
196+ defer s .cond . L . Unlock ()
132197
133198 // Search and remove the subscriber
199+ sub .stop = true
134200 subs := make ([]* consumer [T ], 0 , len (s .subs ))
135201 for _ , v := range s .subs {
136202 if v != sub {
@@ -142,6 +208,8 @@ func (s *group[T]) Del(sub *consumer[T]) {
142208
143209// ------------------------------------- Debugging -------------------------------------
144210
211+ var errClosed = fmt .Errorf ("event dispatcher is closed" )
212+
145213// Count returns the number of subscribers in this group
146214func (s * group [T ]) Count () int {
147215 return len (s .subs )
0 commit comments