@@ -24,6 +24,7 @@ package concurrent
24
24
25
25
import cats .effect ._
26
26
import cats .effect .implicits ._
27
+ import cats .effect .Resource .ExitCase
27
28
import cats .syntax .all ._
28
29
29
30
/** Stream aware, multiple producer, single consumer closeable channel.
@@ -138,76 +139,79 @@ object Channel {
138
139
size : Int ,
139
140
waiting : Option [Deferred [F , Unit ]],
140
141
producers : List [(A , Deferred [F , Unit ])],
141
- closed : Boolean
142
+ closed : Option [ ExitCase ]
142
143
)
143
144
144
- val open = State (List .empty, 0 , None , List .empty, closed = false )
145
+ val open = State (List .empty, 0 , None , List .empty, closed = None )
145
146
146
- def empty (isClosed : Boolean ): State =
147
- if (isClosed ) State (List .empty, 0 , None , List .empty, closed = true )
147
+ def empty (close : Option [ ExitCase ] ): State =
148
+ if (close.nonEmpty ) State (List .empty, 0 , None , List .empty, closed = close )
148
149
else open
149
150
150
151
(F .ref(open), F .deferred[Unit ]).mapN { (state, closedGate) =>
151
152
new Channel [F , A ] {
152
153
153
154
def sendAll : Pipe [F , A , Nothing ] = { in =>
154
- in.onFinalize(close .void)
155
+ in.onFinalizeCase(closeWithExitCase(_) .void)
155
156
.evalMap(send)
156
157
.takeWhile(_.isRight)
157
158
.drain
158
159
}
159
160
160
- def sendImpl (a : A , close : Boolean ) =
161
+ def sendImpl (a : A , close : Option [ ExitCase ] ) =
161
162
F .deferred[Unit ].flatMap { producer =>
162
163
state.flatModifyFull { case (poll, state) =>
163
164
state match {
164
- case s @ State (_, _, _, _, closed @ true ) =>
165
+ case s @ State (_, _, _, _, Some (_) ) =>
165
166
(s, Channel .closed[Unit ].pure[F ])
166
167
167
- case State (values, size, waiting, producers, closed @ false ) =>
168
+ case State (values, size, waiting, producers, None ) =>
168
169
if (size < capacity)
169
170
(
170
171
State (a :: values, size + 1 , None , producers, close),
171
- signalClosure.whenA(close) *> notifyStream(waiting).as(rightUnit)
172
+ signalClosure.whenA(close.nonEmpty ) *> notifyStream(waiting).as(rightUnit)
172
173
)
173
174
else
174
175
(
175
176
State (values, size, None , (a, producer) :: producers, close),
176
- signalClosure.whenA(close) *>
177
+ signalClosure.whenA(close.nonEmpty ) *>
177
178
notifyStream(waiting).as(rightUnit) <*
178
- waitOnBound(producer, poll).unlessA(close)
179
+ waitOnBound(producer, poll).unlessA(close.nonEmpty )
179
180
)
180
181
}
181
182
}
182
183
}
183
184
184
- def send (a : A ) = sendImpl(a, false )
185
+ def send (a : A ) = sendImpl(a, None )
185
186
186
- def closeWithElement (a : A ) = sendImpl(a, true )
187
+ def closeWithElement (a : A ) = sendImpl(a, Some ( ExitCase . Succeeded ) )
187
188
188
189
def trySend (a : A ) =
189
190
state.flatModify {
190
- case s @ State (_, _, _, _, closed @ true ) =>
191
+ case s @ State (_, _, _, _, Some (_) ) =>
191
192
(s, Channel .closed[Boolean ].pure[F ])
192
193
193
- case s @ State (values, size, waiting, producers, closed @ false ) =>
194
+ case s @ State (values, size, waiting, producers, None ) =>
194
195
if (size < capacity)
195
196
(
196
- State (a :: values, size + 1 , None , producers, false ),
197
+ State (a :: values, size + 1 , None , producers, None ),
197
198
notifyStream(waiting).as(rightTrue)
198
199
)
199
200
else
200
201
(s, rightFalse.pure[F ])
201
202
}
202
203
203
204
def close =
205
+ closeWithExitCase(ExitCase .Succeeded )
206
+
207
+ def closeWithExitCase (exitCase : ExitCase ): F [Either [Closed , Unit ]] =
204
208
state.flatModify {
205
- case s @ State (_, _, _, _, closed @ true ) =>
209
+ case s @ State (_, _, _, _, Some (_) ) =>
206
210
(s, Channel .closed[Unit ].pure[F ])
207
211
208
- case State (values, size, waiting, producers, closed @ false ) =>
212
+ case State (values, size, waiting, producers, None ) =>
209
213
(
210
- State (values, size, None , producers, true ),
214
+ State (values, size, None , producers, Some (exitCase) ),
211
215
notifyStream(waiting).as(rightUnit) <* signalClosure
212
216
)
213
217
}
@@ -250,8 +254,12 @@ object Channel {
250
254
unblock.as(Pull .output(toEmit) >> consumeLoop)
251
255
} else {
252
256
F .pure(
253
- if (closed) Pull .done
254
- else Pull .eval(waiting.get) >> consumeLoop
257
+ closed match {
258
+ case Some (ExitCase .Succeeded ) => Pull .done
259
+ case Some (ExitCase .Errored (e)) => Pull .raiseError(e)
260
+ case Some (ExitCase .Canceled ) => Pull .eval(F .canceled)
261
+ case None => Pull .eval(waiting.get) >> consumeLoop
262
+ }
255
263
)
256
264
}
257
265
}
0 commit comments