/
FutureConverters.scala
89 lines (85 loc) · 3.96 KB
/
FutureConverters.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.jdk.javaapi
import java.util.concurrent.{CompletableFuture, CompletionStage}
import scala.concurrent.impl.FutureConvertersImpl.{CF, P}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Success
/** This object contains methods that convert between Scala [[scala.concurrent.Future]] and Java [[java.util.concurrent.CompletionStage]].
*
* The explicit conversion methods defined here are intended to be used in Java code. For Scala
* code, it is recommended to use the extension methods defined in [[scala.jdk.FutureConverters]].
*
* Note that the bridge is implemented at the read-only side of asynchronous handles, namely
* [[scala.concurrent.Future]] (instead of [[scala.concurrent.Promise]]) and [[java.util.concurrent.CompletionStage]] (instead of
* [[java.util.concurrent.CompletableFuture]]). This is intentional, as the semantics of bridging
* the write-handles would be prone to race conditions; if both ends (`CompletableFuture` and
* `Promise`) are completed independently at the same time, they may contain different values
* afterwards. For this reason, `toCompletableFuture` is not supported on the created
* `CompletionStage`s.
*/
object FutureConverters {
/** Returns a [[java.util.concurrent.CompletionStage]] that will be completed with the same value or exception as the
* given Scala [[scala.concurrent.Future]] when that completes. Since the Future is a read-only representation,
* this CompletionStage does not support the `toCompletableFuture` method.
*
* The semantics of Scala Future demand that all callbacks are invoked asynchronously by default,
* therefore the returned CompletionStage routes all calls to synchronous transformations to
* their asynchronous counterparts, i.e., `thenRun` will internally call `thenRunAsync`.
*
* @param f The Scala Future which may eventually supply the completion for the returned
* CompletionStage
* @return a CompletionStage that runs all callbacks asynchronously and does not support the
* CompletableFuture interface
*/
def asJava[T](f: Future[T]): CompletionStage[T] = {
f match {
case p: P[T] => p.wrapped
// in theory not safe (could be `class C extends Future[A] with CompletionStage[B]`):
case c: CompletionStage[T @unchecked] => c
case _ =>
val cf = new CF[T](f)
f.onComplete(cf)(ExecutionContext.parasitic)
cf
}
}
/** Returns a Scala [[scala.concurrent.Future]] that will be completed with the same value or exception as the
* given [[java.util.concurrent.CompletionStage]] when that completes. Transformations of the returned Future are
* executed asynchronously as specified by the ExecutionContext that is given to the combinator
* methods.
*
* @param cs The CompletionStage which may eventually supply the completion for the returned
* Scala Future
* @return a Scala Future that represents the CompletionStage's completion
*/
def asScala[T](cs: CompletionStage[T]): Future[T] = {
cs match {
case cf: CF[T] => cf.wrapped
// in theory not safe (could be `class C extends Future[A] with CompletionStage[B]`):
case f: Future[T @unchecked] => f
case _ =>
val p = new P[T](cs)
val completedCF = cs match {
case cf0: CompletableFuture[T @unchecked] =>
// drop `MinimalStage` (scala/bug#12918)
val cf = cf0.toCompletableFuture
if (cf.isDone && !cf.isCompletedExceptionally) cf else null
case _ => null
}
if (completedCF != null)
p.tryComplete(Success(completedCF.join()))
else
cs.handle(p)
p.future
}
}
}