@@ -5,7 +5,15 @@ import scala.util.boundary, boundary.Label
5
5
import scala .compiletime .uninitialized
6
6
import scala .util .{Try , Success , Failure }
7
7
import java .util .concurrent .atomic .AtomicBoolean
8
- import runtime .*
8
+ import runtime .suspend
9
+
10
+ /** A hypothetical task scheduler trait */
11
+ trait Scheduler :
12
+ def schedule (task : Runnable ): Unit = ???
13
+
14
+ object Scheduler extends Scheduler :
15
+ given fromAsync (using async : Async ): Scheduler = async.client.scheduler
16
+ end Scheduler
9
17
10
18
trait Async :
11
19
@@ -24,15 +32,16 @@ trait Async:
24
32
/** The future computed by this async computation. */
25
33
def client : Future [? ]
26
34
35
+ object Async :
36
+ inline def current (using async : Async ): Async = async
27
37
end Async
28
38
29
- class Future [+ T ](body : Async ?=> T ):
39
+ class Future [+ T ](body : Async ?=> T )( using val scheduler : Scheduler ) :
30
40
import Future .{Status , Cancellation }, Status .*
31
41
32
- @ volatile private var status : Status = Initial
42
+ @ volatile private var status : Status = Started
33
43
private var result : Try [T ] = uninitialized
34
44
private var waiting : ListBuffer [Try [T ] => Unit ] = ListBuffer ()
35
- private var scheduler : Scheduler = uninitialized
36
45
private var children : mutable.Set [Future [? ]] = mutable.Set ()
37
46
38
47
private def addWaiting (k : Try [T ] => Unit ): Unit = synchronized :
@@ -63,9 +72,6 @@ class Future[+T](body: Async ?=> T):
63
72
given Async with
64
73
65
74
private def resultOption [T ](f : Future [T ]): Option [Try [T ]] = f.status match
66
- case Initial =>
67
- f.ensureStarted()(using scheduler)
68
- resultOption(f)
69
75
case Started =>
70
76
None
71
77
case Completed =>
@@ -110,30 +116,13 @@ class Future[+T](body: Async ?=> T):
110
116
111
117
private def complete (): Unit =
112
118
async :
113
- val result =
119
+ result =
114
120
try Success (body)
115
121
catch case ex : Exception => Failure (ex)
116
122
status = Completed
117
- for task <- currentWaiting() do task(result)
118
- cancelChildren()
119
- notifyAll()
120
-
121
- /** Ensure future's execution has started */
122
- def ensureStarted ()(using scheduler : Scheduler ): this .type =
123
- synchronized :
124
- if status == Initial then start()
125
- this
126
-
127
- /** Start future's execution
128
- * @pre future has not yet started
129
- */
130
- def start ()(using scheduler : Scheduler ): this .type =
131
- synchronized :
132
- assert(status == Initial )
133
- this .scheduler = scheduler
134
- scheduler.schedule(() => complete())
135
- status = Started
136
- this
123
+ for task <- currentWaiting() do task(result)
124
+ cancelChildren()
125
+ notifyAll()
137
126
138
127
/** Links the future as a child to the current async client.
139
128
* This means the future will be cancelled when the async client
@@ -162,52 +151,44 @@ class Future[+T](body: Async ?=> T):
162
151
while status != Completed do wait()
163
152
result.get
164
153
165
- object Future :
166
-
167
- class Cancellation extends Exception
154
+ scheduler.schedule(() => complete())
155
+ end Future
168
156
157
+ object Future :
169
158
enum Status :
170
159
// Transitions always go left to right.
171
160
// Cancelled --> Completed with Failure(Cancellation()) result
172
- case Initial , Started , Cancelled , Completed
161
+ case Started , Cancelled , Completed
173
162
174
- /** Construct a future and start it so that ion runs in parallel with the
175
- * current thread.
176
- */
177
- def spawn [T ](body : Async ?=> T )(using Scheduler ): Future [T ] =
178
- Future (body).start()
163
+ class Cancellation extends Exception
164
+ end Future
179
165
180
- /** The conjuntion of two futures with given bodies `body1` and `body2`.
181
- * If both futures succeed, suceed with their values in a pair. Otherwise,
182
- * fail with the failure that was returned first.
183
- */
184
- def both [T1 , T2 ](body1 : Async ?=> T1 , body2 : Async ?=> T2 ): Future [(T1 , T2 )] =
185
- Future : async ?=>
186
- val f1 = Future (body1).linked
187
- val f2 = Future (body2).linked
166
+ class Task [+ T ](val body : Async ?=> T ):
167
+ def run (using Scheduler ): Future [T ] = Future (body)
168
+
169
+ def par [U ](other : Task [U ]): Task [(T , U )] =
170
+ Task : async ?=>
171
+ val f1 = Future (this .body).linked
172
+ val f2 = Future (other.body).linked
188
173
async.awaitEither(f1, f2) match
189
174
case Left (Success (x1)) => (x1, f2.value)
190
175
case Right (Success (x2)) => (f1.value, x2)
191
176
case Left (Failure (ex)) => throw ex
192
177
case Right (Failure (ex)) => throw ex
193
178
194
- /** The disjuntion of two futures with given bodies `body1` and `body2`.
195
- * If either future succeeds, suceed with the success that was returned first.
196
- * Otherwise, fail with the failure that was returned last.
197
- */
198
- def either [T ](body1 : Async ?=> T , body2 : Async ?=> T ): Future [T ] =
199
- Future : async ?=>
200
- val f1 = Future (body1).linked
201
- val f2 = Future (body2).linked
179
+ def alt [U >: T ](other : Task [U ]): Task [U ] =
180
+ Task : async ?=>
181
+ val f1 = Future (this .body).linked
182
+ val f2 = Future (other.body).linked
202
183
async.awaitEither(f1, f2) match
203
184
case Left (Success (x1)) => x1
204
185
case Right (Success (x2)) => x2
205
186
case Left (_ : Failure [? ]) => f2.value
206
187
case Right (_ : Failure [? ]) => f1.value
207
- end Future
188
+ end Task
208
189
209
190
def Test (x : Future [Int ], xs : List [Future [Int ]])(using Scheduler ): Future [Int ] =
210
- Future .spawn :
191
+ Future :
211
192
x.value + xs.map(_.value).sum
212
193
213
194
def Main (x : Future [Int ], xs : List [Future [Int ]])(using Scheduler ): Int =
0 commit comments