1
1
package gears .async
2
2
3
+ import language .experimental .captureChecking
4
+
3
5
import java .util .concurrent .CancellationException
4
6
import java .util .concurrent .atomic .AtomicBoolean
5
7
import scala .annotation .tailrec
6
- import scala .annotation .unchecked .uncheckedCaptures
7
8
import scala .annotation .unchecked .uncheckedVariance
8
9
import scala .collection .mutable
9
10
import scala .compiletime .uninitialized
10
11
import scala .util
11
12
import scala .util .control .NonFatal
12
13
import scala .util .{Failure , Success , Try }
13
-
14
- import language .experimental .captureChecking
14
+ import gears .async .Async .SourceSymbol
15
15
16
16
/** Futures are [[Async.Source Source ]]s that has the following properties:
17
17
* - They represent a single value: Once resolved, [[Async.await await ]]-ing on a [[Future ]] should always return the
@@ -51,10 +51,11 @@ object Future:
51
51
* - withResolver: Completion is done by external request set up from a block of code.
52
52
*/
53
53
private class CoreFuture [+ T ] extends Future [T ]:
54
+
54
55
@ volatile protected var hasCompleted : Boolean = false
55
56
protected var cancelRequest = AtomicBoolean (false )
56
57
private var result : Try [T ] = uninitialized // guaranteed to be set if hasCompleted = true
57
- private val waiting = mutable.Set [( Listener [Try [T ]]^ ) @ uncheckedCaptures] ()
58
+ private val waiting : mutable.Set [Listener [Try [T ]]^ ] = mutable. Set ()
58
59
59
60
// Async.Source method implementations
60
61
@@ -107,49 +108,11 @@ object Future:
107
108
108
109
end CoreFuture
109
110
110
- private class CancelSuspension [U ](val src : Async .Source [U ]^ )(val ac : Async , val suspension : ac.support.Suspension [Try [U ], Unit ]) extends Cancellable :
111
- self : CancelSuspension [U ]^ {src, ac} =>
112
- val listener : Listener [U ]^ {ac} = Listener .acceptingListener[U ]: (x, _) =>
113
- val completedBefore = complete()
114
- if ! completedBefore then
115
- ac.support.resumeAsync(suspension)(Success (x))
116
- unlink()
117
- var completed = false
118
-
119
- def complete () = synchronized :
120
- val completedBefore = completed
121
- completed = true
122
- completedBefore
123
-
124
- override def cancel () =
125
- val completedBefore = complete()
126
- if ! completedBefore then
127
- src.dropListener(listener)
128
- ac.support.resumeAsync(suspension)(Failure (new CancellationException ()))
129
-
130
- private class FutureAsync (val group : CompletionGroup )(using ac : Async , label : ac.support.Label [Unit ]) extends Async (using ac.support):
131
- /** Await a source first by polling it, and, if that fails, by suspending in a onComplete call.
132
- */
133
- override def await [U ](src : Async .Source [U ]^ ): U =
134
- if group.isCancelled then throw new CancellationException ()
135
- src
136
- .poll()
137
- .getOrElse:
138
- val res = ac.support.suspend[Try [U ], Unit ](k =>
139
- val cancellable : CancelSuspension [U ]^ {src, ac} = CancelSuspension (src)(ac, k)
140
- // val listener: Listener[U] = Listener.acceptingListener[U]: (x, _) => ???
141
- // val completedBefore = cancellable.complete()
142
- // if !completedBefore then ac.support.resumeAsync(k)(Success(x))
143
- cancellable.link(group) // may resume + remove listener immediately
144
- src.onComplete(cancellable.listener)
145
- )(using label)
146
- res.get
147
-
148
- override def withGroup (group : CompletionGroup ): Async = FutureAsync (group)
149
-
150
111
/** A future that is completed by evaluating `body` as a separate asynchronous operation in the given `scheduler`
151
112
*/
152
113
private class RunnableFuture [+ T ](body : Async .Spawn ?=> T )(using ac : Async ) extends CoreFuture [T ]:
114
+ private given acSupport : ac.support.type = ac.support
115
+ private given acScheduler : ac.support.Scheduler = ac.scheduler
153
116
/** RunnableFuture maintains its own inner [[CompletionGroup ]], that is separated from the provided Async
154
117
* instance's. When the future is cancelled, we only cancel this CompletionGroup. This effectively means any
155
118
* `.await` operations within the future is cancelled *only if they link into this group*. The future body run with
@@ -160,6 +123,47 @@ object Future:
160
123
private def checkCancellation (): Unit =
161
124
if cancelRequest.get() then throw new CancellationException ()
162
125
126
+ private class FutureAsync (val group : CompletionGroup )(using label : acSupport.Label [Unit ])
127
+ extends Async (using acSupport, acScheduler):
128
+ /** Await a source first by polling it, and, if that fails, by suspending in a onComplete call.
129
+ */
130
+ override def await [U ](src : Async .Source [U ]^ ): U =
131
+ class CancelSuspension extends Cancellable :
132
+ var suspension : acSupport.Suspension [Try [U ], Unit ] = uninitialized
133
+ var listener : Listener [U ]^ {this } = uninitialized
134
+ var completed = false
135
+
136
+ def complete () = synchronized :
137
+ val completedBefore = completed
138
+ completed = true
139
+ completedBefore
140
+
141
+ override def cancel () =
142
+ val completedBefore = complete()
143
+ if ! completedBefore then
144
+ src.dropListener(listener)
145
+ acSupport.resumeAsync(suspension)(Failure (new CancellationException ()))
146
+
147
+ if group.isCancelled then throw new CancellationException ()
148
+
149
+ src
150
+ .poll()
151
+ .getOrElse:
152
+ val cancellable = CancelSuspension ()
153
+ val res = acSupport.suspend[Try [U ], Unit ](k =>
154
+ val listener = Listener .acceptingListener[U ]: (x, _) =>
155
+ val completedBefore = cancellable.complete()
156
+ if ! completedBefore then acSupport.resumeAsync(k)(Success (x))
157
+ cancellable.suspension = k
158
+ cancellable.listener = listener
159
+ cancellable.link(group) // may resume + remove listener immediately
160
+ src.onComplete(listener)
161
+ )
162
+ cancellable.unlink()
163
+ res.get
164
+
165
+ override def withGroup (group : CompletionGroup ): Async = FutureAsync (group)
166
+
163
167
override def cancel (): Unit = if setCancelled() then this .innerGroup.cancel()
164
168
165
169
link()
@@ -178,8 +182,9 @@ object Future:
178
182
/** Create a future that asynchronously executes `body` that wraps its execution in a [[scala.util.Try ]]. The returned
179
183
* future is linked to the given [[Async.Spawn ]] scope by default, i.e. it is cancelled when this scope ends.
180
184
*/
181
- def apply [T ](body : Async .Spawn ?=> T )(using async : Async , spawnable : Async .Spawn )
182
- (using async.type =:= spawnable.type ): Future [T ]^ {body, spawnable} =
185
+ def apply [T ](body : Async .Spawn ?=> T )(using async : Async , spawnable : Async .Spawn )(
186
+ using async.type =:= spawnable.type
187
+ ): Future [T ]^ {body, spawnable} =
183
188
RunnableFuture (body)(using spawnable)
184
189
185
190
/** A future that is immediately completed with the given result. */
@@ -197,11 +202,11 @@ object Future:
197
202
/** A future that immediately rejects with the given exception. Similar to `Future.now(Failure(exception))`. */
198
203
inline def rejected (exception : Throwable ): Future [Nothing ] = now(Failure (exception))
199
204
200
- extension [T ](f1 : Future [T ]^ )
205
+ extension [T ](f1 : Future [T ])
201
206
/** Parallel composition of two futures. If both futures succeed, succeed with their values in a pair. Otherwise,
202
207
* fail with the failure that was returned first.
203
208
*/
204
- def zip [U ](f2 : Future [U ]^ ): Future [(T , U )]^ {f1, f2} =
209
+ def zip [U ](f2 : Future [U ]): Future [(T , U )] =
205
210
Future .withResolver: r =>
206
211
Async
207
212
.either(f1, f2)
@@ -234,20 +239,20 @@ object Future:
234
239
* @see
235
240
* [[orWithCancel ]] for an alternative version where the slower future is cancelled.
236
241
*/
237
- def or (f2 : Future [T ]^ ): Future [T ]^ {f1, f2} = orImpl(false )(f2)
242
+ def or (f2 : Future [T ]): Future [T ] = orImpl(false )(f2)
238
243
239
244
/** Like `or` but the slower future is cancelled. If either task succeeds, succeed with the success that was
240
245
* returned first and the other is cancelled. Otherwise, fail with the failure that was returned last.
241
246
*/
242
- def orWithCancel (f2 : Future [T ]^ ): Future [T ]^ {f1, f2} = orImpl(true )(f2)
247
+ def orWithCancel (f2 : Future [T ]): Future [T ] = orImpl(true )(f2)
243
248
244
- inline def orImpl (inline withCancel : Boolean )(f2 : Future [T ]^ ): Future [T ]^ {f1, f2} = Future .withResolver: r =>
249
+ inline def orImpl (inline withCancel : Boolean )(f2 : Future [T ]): Future [T ] = Future .withResolver: r =>
245
250
Async
246
251
.raceWithOrigin(f1, f2)
247
252
.onComplete(Listener { case ((v, which), _) =>
248
253
v match
249
254
case Success (value) =>
250
- inline if withCancel then (if which == f1.symbol then f2 else f1).cancel()
255
+ inline if withCancel then (if which == f1 then f2 else f1).cancel()
251
256
r.resolve(value)
252
257
case Failure (_) =>
253
258
(if which == f1.symbol then f2 else f1).onComplete(Listener ((v, _) => r.complete(v)))
@@ -300,7 +305,7 @@ object Future:
300
305
* may be used. The handler should eventually complete the Future using one of complete/resolve/reject*. The
301
306
* default handler is set up to [[rejectAsCancelled ]] immediately.
302
307
*/
303
- def onCancel (handler : () - > Unit ): Unit
308
+ def onCancel (handler : () = > Unit ): Unit
304
309
end Resolver
305
310
306
311
/** Create a promise that may be completed asynchronously using external means.
@@ -310,16 +315,16 @@ object Future:
310
315
*
311
316
* If the external operation supports cancellation, the body can register one handler using [[Resolver.onCancel ]].
312
317
*/
313
- def withResolver [T ](body : Resolver [T ]^ => Unit ): Future [T ] =
314
- val future = new CoreFuture [T ] with Resolver [T ] with Promise [T ] {
315
- @ volatile var cancelHandle : (( ) -> Unit ) = () => rejectAsCancelled()
316
- override def onCancel (handler : () - > Unit ): Unit = cancelHandle = handler
318
+ def withResolver [T ](body : Resolver [T ] => Unit ): Future [T ] =
319
+ val future = new CoreFuture [T ] with Resolver [T ] with Promise [T ]:
320
+ @ volatile var cancelHandle : () -> Unit = () => rejectAsCancelled()
321
+ override def onCancel (handler : () = > Unit ): Unit = cancelHandle = caps.unsafe.unsafeAssumePure( handler)
317
322
override def complete (result : Try [T ]): Unit = super .complete(result)
318
323
319
324
override def cancel (): Unit =
320
325
if setCancelled() then cancelHandle()
321
- }
322
- body(future)
326
+ end future
327
+ body(future : Resolver [ T ] )
323
328
future
324
329
end withResolver
325
330
@@ -338,51 +343,46 @@ object Future:
338
343
* [[Future.awaitAll ]] and [[Future.awaitFirst ]] for simple usage of the collectors to get all results or the first
339
344
* succeeding one.
340
345
*/
341
- class Collector [T ](val futures : (Future [T ]^ )* ):
346
+ class Collector [T ](futures : (Future [T ]^ )* ):
342
347
private val ch = UnboundedChannel [Future [T ]^ {futures* }]()
343
348
344
- private val futureRefs = mutable.Map [Async . SourceSymbol [Try [T ]], Future [T ]^ {futures* }]()
349
+ private val futMap = mutable.Map [SourceSymbol [Try [T ]], Future [T ]^ {futures* }]()
345
350
346
351
/** Output channels of all finished futures. */
347
352
final def results : ReadableChannel [Future [T ]^ {futures* }] = ch.asReadable
348
353
349
- private val listener = Listener ((_, futRef ) =>
354
+ private val listener = Listener ((_, fut ) =>
350
355
// safe, as we only attach this listener to Future[T]
351
- val ref = futRef.asInstanceOf [Async .SourceSymbol [Try [T ]]]
352
- val fut = futureRefs.synchronized :
353
- // futureRefs.remove(ref).get
354
- futureRefs(ref)
355
- ch.sendImmediately(futureRefs(fut.symbol))
356
+ val future = futMap.synchronized :
357
+ futMap.remove(fut.asInstanceOf [SourceSymbol [Try [T ]]]).get
358
+ ch.sendImmediately(future)
356
359
)
357
360
358
361
protected final def addFuture (future : Future [T ]^ {futures* }) =
359
- futureRefs.synchronized :
360
- futureRefs += (future.symbol -> future)
362
+ futMap.synchronized { futMap += (future.symbol -> future) }
361
363
future.onComplete(listener)
362
364
363
365
futures.foreach(addFuture)
364
366
end Collector
365
367
366
368
/** Like [[Collector ]], but exposes the ability to add futures after creation. */
367
- class MutableCollector [T ](futures : ( Future [T ]^ ) * ) extends Collector [T ](futures* ):
369
+ class MutableCollector [T ](futures : Future [T ]* ) extends Collector [T ](futures* ):
368
370
/** Add a new [[Future ]] into the collector. */
369
- def add (future : Future [T ]^ {futures * }) : Unit = addFuture(future)
370
- def += (future : Future [T ]^ {futures * } ) = add(future)
371
+ inline def add (future : Future [T ]^ ) = addFuture(future)
372
+ inline def += (future : Future [T ]^ ) = add(future)
371
373
372
- extension [T ](@ caps.unbox fs : Seq [Future [T ]^ ])
374
+ extension [T ](fs : Seq [Future [T ]])
373
375
/** `.await` for all futures in the sequence, returns the results in a sequence, or throws if any futures fail. */
374
376
def awaitAll (using Async ) =
375
377
val collector = Collector (fs* )
376
- for _ <- fs do
377
- val fut : Future [T ]^ {fs* } = collector.results.read().right.get
378
- fut.await
378
+ for _ <- fs do collector.results.read().right.get.await
379
379
fs.map(_.await)
380
380
381
381
/** Like [[awaitAll ]], but cancels all futures as soon as one of them fails. */
382
382
def awaitAllOrCancel (using Async ) =
383
- val collector = Collector [ T ] (fs* )
383
+ val collector = Collector (fs* )
384
384
try
385
- for _ <- fs do ??? // collector.results.read().right.get.await
385
+ for _ <- fs do collector.results.read().right.get.await
386
386
fs.map(_.await)
387
387
catch
388
388
case NonFatal (e) =>
@@ -391,22 +391,20 @@ object Future:
391
391
392
392
/** Race all futures, returning the first successful value. Throws the last exception received, if everything fails.
393
393
*/
394
- def awaitFirst (using Async ): T = impl. awaitFirstImpl[ T ](fs, false )
394
+ def awaitFirst (using Async ): T = awaitFirstImpl( false )
395
395
396
396
/** Like [[awaitFirst ]], but cancels all other futures as soon as the first future succeeds. */
397
- def awaitFirstWithCancel (using Async ): T = impl. awaitFirstImpl[ T ](fs, true )
397
+ def awaitFirstWithCancel (using Async ): T = awaitFirstImpl( true )
398
398
399
- private object impl :
400
- def awaitFirstImpl [T ](@ caps.unbox fs : Seq [Future [T ]^ ], withCancel : Boolean )(using Async ): T =
401
- val collector = Collector [T ](fs* )
399
+ private inline def awaitFirstImpl (withCancel : Boolean )(using Async ): T =
400
+ val collector = Collector (fs* )
402
401
@ scala.annotation.tailrec
403
402
def loop (attempt : Int ): T =
404
- val fut : Future [T ]^ {fs* } = collector.results.read().right.get
405
- fut.awaitResult match
403
+ collector.results.read().right.get.awaitResult match
406
404
case Failure (exception) =>
407
405
if attempt == fs.length then /* everything failed */ throw exception else loop(attempt + 1 )
408
406
case Success (value) =>
409
- if withCancel then fs.foreach(_.cancel())
407
+ inline if withCancel then fs.foreach(_.cancel())
410
408
value
411
409
loop(1 )
412
410
end Future
@@ -432,11 +430,10 @@ class Task[+T](val body: (Async, AsyncOperations) ?=> T):
432
430
def run ()(using Async , AsyncOperations ): T = body
433
431
434
432
/** Start a future computed from the `body` of this task */
435
- def start ()(using async : Async , spawn : Async .Spawn , asyncOps : AsyncOperations )
436
- (using async.type =:= spawn.type ): Future [T ]^ {this , spawn} =
433
+ def start ()(using async : Async , spawn : Async .Spawn )(using asyncOps : AsyncOperations )(using async.type =:= spawn.type ): Future [T ]^ {body, spawn} =
437
434
Future (body)(using async, spawn)
438
435
439
- def schedule (s : TaskSchedule ): Task [T ]^ {this } =
436
+ def schedule (s : TaskSchedule ): Task [T ]^ {body } =
440
437
s match {
441
438
case TaskSchedule .Every (millis, maxRepetitions) =>
442
439
assert(millis >= 1 )
0 commit comments