@@ -60,6 +60,10 @@ abstract class Topic[F[_], A] { self =>
6060 * Note: if `publish1` is called concurrently by multiple producers,
6161 * different subscribers may receive messages from different producers
6262 * in a different order.
63+ *
64+ * Note: if `publish1` returns `Left(Topic.Closed)`, it is possible
65+ * that some subscribers received the event while others did not due
66+ * to concurrent closure.
6367 */
6468 def publish1 (a : A ): F [Either [Topic .Closed , Unit ]]
6569
@@ -152,47 +156,33 @@ object Topic {
152156 (
153157 F .ref(State .initial[F , A ]),
154158 SignallingRef [F , Int ](0 ),
155- F .deferred[Unit ],
156159 F .deferred[Unit ]
157- ).mapN { case (state, subscriberCount, signalClosure, publishersFinished ) =>
160+ ).mapN { case (state, subscriberCount, signalClosure) =>
158161 new Topic [F , A ] {
159162
160163 def foreach [B ](lm : LongMap [B ])(f : B => F [Unit ]) =
161- lm.foldLeft(F .unit) { case (op, (_, b)) => f(b) >> op }
164+ lm.foldLeft(F .unit) { case (op, (_, b)) => op >> f(b) }
162165
163166 def publish1 (a : A ): F [Either [Topic .Closed , Unit ]] =
164- state.flatModify {
165- case s @ State .Active (subs, _, n, false ) =>
166- val inc = n + 1
167- val newState = s.copy(publishing = inc)
168-
169- val sends = subs.foldLeft(F .pure(true )) { case (acc, (_, chan)) =>
170- chan.send(a).map(_.isRight).map2(acc)(_ && _)
171- }
172-
173- val action = sends.flatMap { allSucceeded =>
174- state
175- .flatModify {
176- case s @ State .Active (subs, _, n, closing) =>
177- val dec = n - 1
178- if (dec == 0 && closing) {
179- val closeAction = foreach(subs)(_.close.void)
180- (State .Closed (), closeAction >> publishersFinished.complete(()).void)
181- } else {
182- (s.copy(publishing = dec), F .unit)
167+ state.get.flatMap {
168+ case State .Closed () =>
169+ Topic .closed.pure[F ]
170+ case State .Active (subs, _) =>
171+ subs.foldLeft(Topic .rightUnit.pure[F ]: F [Either [Topic .Closed , Unit ]]) {
172+ case (acc, (_, chan)) =>
173+ acc.flatMap {
174+ case Left (Topic .Closed ) => Topic .closed.pure[F ]
175+ case Right (_) =>
176+ chan.send(a).flatMap {
177+ case Right (_) => Topic .rightUnit.pure[F ]
178+ case Left (_) =>
179+ state.get.map {
180+ case State .Closed () => Topic .closed
181+ case _ => Topic .rightUnit
182+ }
183183 }
184- case s @ State .Closed () => (s, F .unit)
185- }
186- .map { _ =>
187- if (allSucceeded) Topic .rightUnit else Topic .closed
188184 }
189185 }
190- (newState, action)
191-
192- case s @ State .Active (_, _, _, true ) =>
193- (s, Topic .closed.pure[F ])
194- case s @ State .Closed () =>
195- (s, Topic .closed.pure[F ])
196186 }
197187
198188 def subscribeAwait (maxQueued : Int ): Resource [F , Stream [F , A ]] =
@@ -208,20 +198,18 @@ object Topic {
208198 def subscribeAwaitImpl (chan : Channel [F , A ]): Resource [F , Stream [F , A ]] = {
209199 val subscribe : F [Option [Long ]] =
210200 state.flatModify {
211- case s @ State .Active (subs, nextId, _, false ) =>
212- val newState = s.copy(subscribers = subs.updated(nextId, chan), nextId = nextId + 1 )
201+ case State .Active (subs, nextId) =>
202+ val newState = State . Active ( subs.updated(nextId, chan), nextId + 1 )
213203 val action = subscriberCount.update(_ + 1 )
214204 val result = Some (nextId)
215205 newState -> action.as(result)
216- case s @ State .Active (_, _, _, true ) =>
217- s -> F .pure(None )
218206 case closed @ State .Closed () =>
219207 closed -> F .pure(None )
220208 }
221209
222210 def unsubscribe (id : Long ): F [Unit ] =
223211 state.flatModify {
224- case s @ State .Active (subs, _, _, _ ) =>
212+ case State .Active (subs, nextId ) =>
225213 // _After_ we remove the bounded channel for this
226214 // subscriber, we need to drain it to unblock to
227215 // publish loop which might have already enqueued
@@ -231,7 +219,7 @@ object Topic {
231219 chan.close >> chan.stream.compile.drain
232220 }
233221
234- s.copy(subscribers = subs - id) -> (drainChannel *> subscriberCount.update(_ - 1 ))
222+ State . Active ( subs - id, nextId ) -> (drainChannel *> subscriberCount.update(_ - 1 ))
235223
236224 case closed @ State .Closed () =>
237225 closed -> F .unit
@@ -265,15 +253,9 @@ object Topic {
265253
266254 def close : F [Either [Topic .Closed , Unit ]] =
267255 state.flatModify {
268- case s @ State .Active (subs, _, n, false ) =>
269- if (n == 0 ) {
270- val action = foreach(subs)(_.close.void) *> signalClosure.complete(())
271- (State .Closed (), (action >> publishersFinished.complete(())).as(Topic .rightUnit))
272- } else {
273- (s.copy(closing = true ), publishersFinished.get.as(Topic .rightUnit))
274- }
275- case s @ State .Active (_, _, _, true ) =>
276- (s, publishersFinished.get.as(Topic .rightUnit))
256+ case State .Active (subs, _) =>
257+ val action = foreach(subs)(_.close.void) *> signalClosure.complete(())
258+ (State .Closed (), action.as(Topic .rightUnit))
277259 case closed @ State .Closed () =>
278260 (closed, Topic .closed.pure[F ])
279261 }
@@ -288,15 +270,13 @@ object Topic {
288270 private object State {
289271 case class Active [F [_], A ](
290272 subscribers : LongMap [Channel [F , A ]],
291- nextId : Long ,
292- publishing : Long ,
293- closing : Boolean
273+ nextId : Long
294274 ) extends State [F , A ]
295275
296276 case class Closed [F [_], A ]() extends State [F , A ]
297277
298278 def initial [F [_], A ]: State [F , A ] =
299- Active (LongMap .empty, 1L , 0L , false )
279+ Active (LongMap .empty, 1L )
300280 }
301281
302282 private final val closed : Either [Closed , Unit ] = Left (Closed )
0 commit comments