@@ -14,13 +14,16 @@ import jsonrpclib.internals.MessageDispatcher
14
14
import jsonrpclib .internals ._
15
15
16
16
import scala .util .Try
17
+ import _root_ .fs2 .concurrent .SignallingRef
17
18
18
19
trait FS2Channel [F [_]] extends Channel [F ] {
19
20
def withEndpoint (endpoint : Endpoint [F ])(implicit F : Functor [F ]): Resource [F , Unit ] =
20
21
Resource .make(mountEndpoint(endpoint))(_ => unmountEndpoint(endpoint.method))
21
22
22
23
def withEndpoints (endpoint : Endpoint [F ], rest : Endpoint [F ]* )(implicit F : Monad [F ]): Resource [F , Unit ] =
23
24
(endpoint :: rest.toList).traverse_(withEndpoint)
25
+
26
+ def open : Resource [F , Unit ]
24
27
}
25
28
26
29
object FS2Channel {
@@ -42,16 +45,22 @@ object FS2Channel {
42
45
val endpointsMap = startingEndpoints.map(ep => ep.method -> ep).toMap
43
46
for {
44
47
supervisor <- Stream .resource(Supervisor [F ])
45
- ref <- Ref [F ].of(State [F ](Map .empty, endpointsMap, 0 )).toStream
46
- impl = new Impl (payloadSink, ref, supervisor)
47
- _ <- Stream (()).concurrently(payloadStream.evalMap(impl.handleReceivedPayload))
48
+ ref <- Ref [F ].of(State [F ](Map .empty, endpointsMap, 0 , false )).toStream
49
+ isOpen <- SignallingRef [F ].of(false ).toStream
50
+ impl = new Impl (payloadSink, ref, isOpen, supervisor)
51
+ _ <- Stream (()).concurrently {
52
+ // Gatekeeping the pull until the channel is actually marked as open
53
+ val wait = isOpen.waitUntil(identity)
54
+ payloadStream.evalTap(_ => wait).evalMap(impl.handleReceivedPayload)
55
+ }
48
56
} yield impl
49
57
}
50
58
51
59
private case class State [F [_]](
52
60
pendingCalls : Map [CallId , OutputMessage => F [Unit ]],
53
61
endpoints : Map [String , Endpoint [F ]],
54
- counter : Long
62
+ counter : Long ,
63
+ isOpen : Boolean
55
64
) {
56
65
def nextCallId : (State [F ], CallId ) = (this .copy(counter = counter + 1 ), CallId .NumberId (counter))
57
66
def storePendingCall (callId : CallId , handle : OutputMessage => F [Unit ]): State [F ] =
@@ -67,11 +76,15 @@ object FS2Channel {
67
76
}
68
77
def removeEndpoint (method : String ): State [F ] =
69
78
copy(endpoints = endpoints.removed(method))
79
+
80
+ def open : State [F ] = copy(isOpen = true )
81
+ def close : State [F ] = copy(isOpen = false )
70
82
}
71
83
72
84
private class Impl [F [_]](
73
85
private val sink : Payload => F [Unit ],
74
86
private val state : Ref [F , FS2Channel .State [F ]],
87
+ private val isOpen : SignallingRef [F , Boolean ],
75
88
supervisor : Supervisor [F ]
76
89
)(implicit F : Concurrent [F ])
77
90
extends MessageDispatcher [F ]
@@ -88,6 +101,8 @@ object FS2Channel {
88
101
89
102
def unmountEndpoint (method : String ): F [Unit ] = state.update(_.removeEndpoint(method))
90
103
104
+ def open : Resource [F , Unit ] = Resource .make[F , Unit ](isOpen.set(true ))(_ => isOpen.set(false ))
105
+
91
106
protected def background [A ](fa : F [A ]): F [Unit ] = supervisor.supervise(fa).void
92
107
protected def reportError (params : Option [Payload ], error : ProtocolError , method : String ): F [Unit ] = ???
93
108
protected def getEndpoint (method : String ): F [Option [Endpoint [F ]]] = state.get.map(_.endpoints.get(method))
0 commit comments