@@ -2,10 +2,14 @@ package concurrent
22import scala .collection .mutable , mutable .ListBuffer
33import scala .util .boundary .Label
44import runtime .suspend
5- import Async .{Listener , await }
5+ import Async .{Listener , await , Yes }
6+
7+ /** An unbounded channel
8+ * Unbounded channels are composable async sources.
9+ */
10+ class UnboundedChannel [T ] extends Async .Source [T ]:
11+ type CanFilter = Yes
612
7- /** An unbounded channel */
8- class UnboundedChannel [T ] extends Async .ComposableSource [T ]:
913 private val pending = ListBuffer [T ]()
1014 private val waiting = mutable.Set [Listener [T ]]()
1115
@@ -45,69 +49,108 @@ class UnboundedChannel[T] extends Async.ComposableSource[T]:
4549
4650end UnboundedChannel
4751
52+ /** An unbuffered, synchronous channel. Senders and readers both block
53+ * until a communication between them happens.
54+ * The channel provides two async sources, one for reading and one for
55+ * sending. The two sources are not composable. This allows a simple
56+ * implementation strategy where at each point either some senders
57+ * are waiting for matching readers, or some readers are waiting for matching
58+ * senders, or the channel is idle, i.e. there are no waiting readers or senders.
59+ * If a send operation encounters some waiting readers, or a read operation
60+ * encounters some waiting sender the data is transmitted directly. Otherwise
61+ * we add the operation to the corresponding waiting pending set.
62+ */
4863trait SyncChannel [T ]:
49- def canRead : Async .Source [T ]
50- def canSend : Async .Source [Listener [T ]]
64+ thisCannel =>
65+
66+ type CanFilter
67+
68+ val canRead : Async .Source [T ] { type CanFilter = thisCannel.CanFilter }
69+ val canSend : Async .Source [Listener [T ]] { type CanFilter = thisCannel.CanFilter }
5170
5271 def send (x : T )(using Async ): Unit = await(canSend)(x)
5372
5473 def read ()(using Async ): T = await(canRead)
5574
5675object SyncChannel :
57- def apply [T ](): SyncChannel [T ] = new Impl [T ]:
58- val canRead = new ReadSource
59- val canSend = new SendSource
76+ def apply [T ](): SyncChannel [T ] = Impl [T ]()
77+
78+ class Impl [ T ] extends SyncChannel [ T ] :
6079
61- abstract class Impl [T ] extends SyncChannel [T ]:
62- protected val pendingReads = mutable.Set [Listener [T ]]()
63- protected val pendingSends = mutable.Set [Listener [Listener [T ]]]()
80+ private val pendingReads = mutable.Set [Listener [T ]]()
81+ private val pendingSends = mutable.Set [Listener [Listener [T ]]]()
6482
6583 protected def link [T ](pending : mutable.Set [T ], op : T => Boolean ): Boolean =
6684 pending.headOption match
67- case Some (elem) => op(elem); true
85+ case Some (elem) =>
86+ val ok = op(elem)
87+ if ! ok then
88+ // Since sources are not filterable, we can be here only if a race
89+ // was lost and the entry was not yet removed. In that case, remove
90+ // it here.
91+ pending -= pending.head
92+ link(pending, op)
93+ ok
6894 case None => false
6995
7096 private def collapse [T ](k2 : Listener [Listener [T ]]): Option [T ] =
7197 var r : Option [T ] = None
7298 if k2 { x => r = Some (x); true } then r else None
7399
74- protected class ReadSource extends Async .Source [T ]:
100+ private class ReadSource extends Async .Source [T ]:
101+ type CanFilter = Impl .this .CanFilter
75102 def poll (k : Listener [T ]): Boolean =
76103 link(pendingSends, sender => collapse(sender).map(k) == Some (true ))
77104 def onComplete (k : Listener [T ]): Unit =
78105 if ! poll(k) then pendingReads += k
79106 def dropListener (k : Listener [T ]): Unit =
80107 pendingReads -= k
81108
82- protected class SendSource extends Async .Source [Listener [T ]]:
109+ private class SendSource extends Async .Source [Listener [T ]]:
110+ type CanFilter = Impl .this .CanFilter
83111 def poll (k : Listener [Listener [T ]]): Boolean =
84112 link(pendingReads, k(_))
85113 def onComplete (k : Listener [Listener [T ]]): Unit =
86114 if ! poll(k) then pendingSends += k
87115 def dropListener (k : Listener [Listener [T ]]): Unit =
88116 pendingSends -= k
89- end Impl
90117
118+ val canRead = new ReadSource
119+ val canSend = new SendSource
120+ end Impl
91121end SyncChannel
92122
93- trait ComposableSyncChannel [T ] extends SyncChannel [T ]:
94- def canRead : Async .ComposableSource [T ]
95- def canSend : Async .ComposableSource [Listener [T ]]
96-
97- object ComposableSyncChannel :
98- def apply [T ](): ComposableSyncChannel [T ] = new Impl [T ]:
99- val canRead = new ComposableReadSource
100- val canSend = new ComposableSendSource
101-
102- abstract class Impl [T ] extends SyncChannel .Impl [T ], ComposableSyncChannel [T ]:
123+ object FilterableSyncChannel :
124+ def apply [T ](): SyncChannel [T ] { type CanFilter = Yes } = Impl [T ]()
103125
126+ class Impl [T ] extends SyncChannel .Impl [T ]:
127+ type CanFilter = Yes
104128 override protected def link [T ](pending : mutable.Set [T ], op : T => Boolean ): Boolean =
129+ // Since sources are filterable, we have to match all pending readers or writers
130+ // against the incoming request
105131 pending.iterator.find(op) match
106132 case Some (elem) => pending -= elem; true
107133 case None => false
108134
109- class ComposableReadSource extends ReadSource , Async .ComposableSource [T ]
110- class ComposableSendSource extends SendSource , Async .ComposableSource [Listener [T ]]
111- end Impl
112-
113- end ComposableSyncChannel
135+ end FilterableSyncChannel
136+
137+ def TestRace =
138+ val c1, c2 = FilterableSyncChannel [Int ]()
139+ val s = c1.canSend
140+ val c3 = Async .race(c1.canRead, c2.canRead)
141+ val c4 = c3.filter(_ >= 0 )
142+ val d0 = SyncChannel [Int ]()
143+ val d1 = Async .race(c1.canRead, c2.canRead, d0.canRead)
144+ val d2 = d1.map(_ + 1 )
145+ val c5 = Async .either(c1.canRead, c2.canRead)
146+ .map:
147+ case Left (x) => - x
148+ case Right (x) => x
149+ .filter(_ >= 0 )
150+
151+ // val d3bad = d1.filter(_ >= 0)
152+ val d5 = Async .either(c1.canRead, d2)
153+ .map:
154+ case Left (x) => - x
155+ case Right (x) => x
156+ // val d6bad = d5.filter(_ >= 0)
0 commit comments