Skip to content

More efficient round trip conversions for Futures #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ script:
- admin/build.sh
scala:
- 2.11.7
- 2.12.0-M2
# - 2.12.0-M3
jdk:
- oraclejdk8
notifications:
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def jwrite(dir: java.io.File)(name: String, content: String) = {

lazy val commonSettings = Seq(
scalaVersion := "2.11.7",
crossScalaVersions := List("2.11.7", "2.12.0-M2"),
crossScalaVersions := List("2.11.7" /* TODO, "2.12.0-M3"*/),
organization := "org.scala-lang.modules",
version := "0.6.0-SNAPSHOT",
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
Expand Down
26 changes: 14 additions & 12 deletions src/main/scala/scala/compat/java8/FutureConverters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,14 @@ object FutureConverters {
* not support the CompletableFuture interface
*/
def toJava[T](f: Future[T]): CompletionStage[T] = {
val cf = new CF[T]
implicit val ec = InternalCallbackExecutor
f onComplete cf
cf
f match {
case p: P[T] => p.wrapped
case _ =>
val cf = new CF[T](f)
implicit val ec = InternalCallbackExecutor
f onComplete cf
cf
}
}

/**
Expand All @@ -71,15 +75,13 @@ object FutureConverters {
* @return a Scala Future that represents the CompletionStage's completion
*/
def toScala[T](cs: CompletionStage[T]): Future[T] = {
val p = Promise[T]()
val bc = new BiConsumer[T, Throwable] {
override def accept(v: T, e: Throwable): Unit = {
if (e == null) p.complete(Success(v))
else p.complete(Failure(e))
}
cs match {
case cf: CF[T] => cf.wrapped
case _ =>
val p = new P[T](cs)
cs whenComplete p
p.future
}
cs whenComplete bc
p.future
}

/**
Expand Down
14 changes: 12 additions & 2 deletions src/main/scala/scala/concurrent/java8/FutureConvertersImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ package scala.concurrent.java8

// Located in this package to access private[concurrent] members

import scala.concurrent.{ Future, Promise, ExecutionContext, ExecutionContextExecutorService, ExecutionContextExecutor, impl }
import scala.concurrent.{ Future, ExecutionContext }
import java.util.concurrent._

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't you shadowing scala.concurrent.Future now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, wildcards have a lower precendence:

scala> {import scala.concurrent.Future; import java.util.concurrent._; 0 : Future[_]}
<console>:8: error: type mismatch;
 found   : Int(0)
 required: scala.concurrent.Future[_]
              {import scala.concurrent.Future; import java.util.concurrent._; 0 : Future[_]}
                                                                              ^

import scala.concurrent.impl.Promise.DefaultPromise
import scala.util.{ Try, Success, Failure }
import java.util.function.{ BiConsumer, Function ⇒ JF, Consumer, BiFunction }

// TODO: make thie private[scala] when genjavadoc allows for that.
object FuturesConvertersImpl {
def InternalCallbackExecutor = Future.InternalCallbackExecutor

class CF[T] extends CompletableFuture[T] with (Try[T] => Unit) {
class CF[T](val wrapped: Future[T]) extends CompletableFuture[T] with (Try[T] => Unit) {
override def apply(t: Try[T]): Unit = t match {
case Success(v) ⇒ complete(v)
case Failure(e) ⇒ completeExceptionally(e)
Expand Down Expand Up @@ -84,4 +85,13 @@ object FuturesConvertersImpl {

override def toString: String = super[CompletableFuture].toString
}

class P[T](val wrapped: CompletionStage[T]) extends DefaultPromise[T] with BiConsumer[T, Throwable] {
override def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit = super.onSuccess(pf)

override def accept(v: T, e: Throwable): Unit = {
if (e == null) complete(Success(v))
else complete(Failure(e))
}
}
}
19 changes: 19 additions & 0 deletions src/test/java/scala/compat/java8/FutureConvertersTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.*;
import static org.junit.Assert.assertSame;
import static scala.compat.java8.FutureConverters.*;

public class FutureConvertersTest {
Expand Down Expand Up @@ -398,4 +399,22 @@ public void testToJavaToCompletableFutureJavaObtrudeCalledBeforeScalaComplete()
// okay
}
}

@Test
public void testToJavaAndBackAvoidsWrappers() {
final Promise<String> p = promise();
final Future<String> sf = p.future();
final CompletionStage<String> cs = toJava(sf);
Future<String> sf1 = toScala(cs);
assertSame(sf, sf1);
}

@Test
public void testToScalaAndBackAvoidsWrappers() {
final CompletableFuture<String> cf = new CompletableFuture<>();
final Future<String> f = toScala(cf);
CompletionStage<String> cs1 = toJava(f);
assertSame(cf, cs1);

}
}