@@ -12,7 +12,7 @@ import (
1212// Feed defines a set of subscriptions per topic T which receive messages sent
1313// to the Feed.
1414type Feed [T comparable , M any ] struct {
15- subscriptions map [T ][]* subscription [M ]
15+ subscriptions map [T ][]* subscription [T , M ]
1616 mu sync.RWMutex
1717
1818 wg sync.WaitGroup
@@ -23,16 +23,16 @@ type Feed[T comparable, M any] struct {
2323// NewFeed constructs new Feed with topic type T and message type M.
2424func NewFeed [T comparable , M any ]() * Feed [T , M ] {
2525 return & Feed [T , M ]{
26- subscriptions : make (map [T ][]* subscription [M ]),
26+ subscriptions : make (map [T ][]* subscription [T , M ]),
2727 quit : make (chan struct {}),
2828 }
2929}
3030
3131// Subscribe returns a channel from which messages M, that are sent to the Feed
32- // on the same topic, can be read from. Message delivery is guaranteed, so the
33- // channel should be read to avoid possible high number of goroutines. After
34- // cancel function call, all resources ang goroutines are released even if not
35- // all messages are read from channel.
32+ // on the same topic, can be read from. Message delivery preserves ordering and
33+ // is guaranteed, so the channel should be read to avoid keeping unread messages
34+ // in memory. After cancel function call, all resources ang goroutines are
35+ // released even if not all messages are read from channel.
3636func (f * Feed [T , M ]) Subscribe (topic T ) (c <- chan M , cancel func ()) {
3737 channel := make (chan M , 1 ) // buffer 1 not to block on Send method
3838
@@ -46,14 +46,14 @@ func (f *Feed[T, M]) Subscribe(topic T) (c <-chan M, cancel func()) {
4646 f .mu .Lock ()
4747 defer f .mu .Unlock ()
4848
49- s := newSubscription (channel )
49+ s := newSubscription (f , channel )
5050
5151 f .subscriptions [topic ] = append (f .subscriptions [topic ], s )
5252
5353 return channel , func () { f .unsubscribe (topic , s ) }
5454}
5555
56- func (f * Feed [T , M ]) unsubscribe (topic T , s * subscription [M ]) {
56+ func (f * Feed [T , M ]) unsubscribe (topic T , s * subscription [T , M ]) {
5757 f .mu .Lock ()
5858 defer f .mu .Unlock ()
5959
@@ -94,55 +94,85 @@ func (f *Feed[T, M]) Send(topic T, message M) (n int) {
9494 defer f .mu .RUnlock ()
9595
9696 for _ , s := range f .subscriptions [topic ] {
97- // try to send message to the channel
98- select {
99- case s .channel <- message :
100- case <- s .quit :
101- return
102- case <- f .quit :
103- return
104- default :
105- // if channel is blocked,
106- // wait in goroutine to send the message
107- s := s
108-
109- f .wg .Add (1 )
110- go func () {
111- defer f .wg .Done ()
112-
113- select {
114- case s .channel <- message :
115- case <- s .quit :
116- return
117- case <- f .quit :
118- return
119- }
120- }()
121- }
97+ s .send (message )
12298
12399 n ++
124100 }
125101
126102 return n
127103}
128104
129- type subscription [M any ] struct {
130- channel chan M
131- quitOnce sync.Once
132- quit chan struct {}
133- wg sync.WaitGroup
105+ type subscription [T comparable , M any ] struct {
106+ feed * Feed [T , M ]
107+
108+ channel chan M
109+
110+ buf []M
111+ mu sync.Mutex
112+
113+ quit chan struct {}
114+ wg sync.WaitGroup
134115}
135116
136- func newSubscription [M any ](channel chan M ) * subscription [M ] {
137- return & subscription [M ]{
117+ func newSubscription [T comparable , M any ](feed * Feed [T , M ], channel chan M ) * subscription [T , M ] {
118+ return & subscription [T , M ]{
119+ feed : feed ,
138120 channel : channel ,
121+ buf : make ([]M , 0 ),
139122 quit : make (chan struct {}),
140123 }
141124}
142125
143- func (s * subscription [M ]) close () {
144- s .quitOnce .Do (func () {
145- close (s .quit )
146- })
126+ func (s * subscription [T , M ]) send (message M ) {
127+ s .mu .Lock ()
128+ defer s .mu .Unlock ()
129+
130+ if len (s .buf ) > 0 {
131+ s .buf = append (s .buf , message )
132+ return
133+ }
134+
135+ select {
136+ case s .channel <- message :
137+ case <- s .quit :
138+ return
139+ case <- s .feed .quit :
140+ return
141+ default :
142+ s .buf = append (s .buf , message )
143+
144+ s .wg .Add (1 )
145+ go func () {
146+ defer s .wg .Done ()
147+
148+ for {
149+ s .mu .Lock ()
150+ message := s .buf [0 ]
151+ s .mu .Unlock ()
152+
153+ select {
154+ case s .channel <- message :
155+ case <- s .quit :
156+ return
157+ case <- s .feed .quit :
158+ return
159+ }
160+
161+ s .mu .Lock ()
162+ s .buf = s .buf [1 :]
163+ if len (s .buf ) == 0 {
164+ s .mu .Unlock ()
165+ return
166+ }
167+ s .mu .Unlock ()
168+ }
169+
170+ }()
171+ }
172+ }
173+
174+ func (s * subscription [T , M ]) close () {
175+ close (s .quit )
147176 s .wg .Wait ()
177+ close (s .channel )
148178}
0 commit comments