Permalink
Browse files

SIP-14 backport to 2.9.x

Other than adding the new APIs, a few changes are required:

1. Build. The Java sources in "scala/concurrent/impl" need forkjoin.jar
   on the classpath. Thus, I replaced the "classpath" attribute in the
   respective `javac` ant tasks (locker.lib, quick.lib, and strap.lib)
   with a "classpathref" attribute pointing to a classpath which includes
   also the forkjoin.jar. In the locker.lib target, exclude the duration
   package and everything that depends on it. In the docs.lib target,
   enable dependent method types, since they're used in the
   `scala.concurrent.duration` package.

   Also, the library is built with -Ydependent-method-types
   (for both quick & strap -- use consistent options for stability)

2. Dependent method types and SI-5958. The `duration` package relies on
   dependent method types, and requires a backport of SI-5958.
  • Loading branch information...
1 parent 32782cd commit 61862d2454d9960242dad7df523177755b0fd0d5 @phaller phaller committed with adriaanm Dec 7, 2012
Showing with 4,898 additions and 9 deletions.
  1. +20 −6 build.xml
  2. +64 −0 src/library/scala/concurrent/Awaitable.scala
  3. +77 −0 src/library/scala/concurrent/BlockContext.scala
  4. +1 −3 src/library/scala/concurrent/DelayedLazyVal.scala
  5. +88 −0 src/library/scala/concurrent/ExecutionContext.scala
  6. +691 −0 src/library/scala/concurrent/Future.scala
  7. +152 −0 src/library/scala/concurrent/Promise.scala
  8. +81 −0 src/library/scala/concurrent/duration/Deadline.scala
  9. +697 −0 src/library/scala/concurrent/duration/Duration.scala
  10. +92 −0 src/library/scala/concurrent/duration/DurationConversions.scala
  11. +79 −0 src/library/scala/concurrent/duration/package.scala
  12. +40 −0 src/library/scala/concurrent/impl/AbstractPromise.java
  13. +21 −0 src/library/scala/concurrent/impl/AdaptedRunnableAction.java
  14. +135 −0 src/library/scala/concurrent/impl/ExecutionContextImpl.scala
  15. +34 −0 src/library/scala/concurrent/impl/Future.scala
  16. +174 −0 src/library/scala/concurrent/impl/Promise.scala
  17. +109 −0 src/library/scala/concurrent/package.scala
  18. +35 −0 src/library/scala/concurrent/util/Unsafe.java
  19. +216 −0 src/library/scala/util/Try.scala
  20. +45 −0 src/library/scala/util/control/NonFatal.scala
  21. +1 −0 test/files/jvm/future-spec.flags
  22. +524 −0 test/files/jvm/future-spec/FutureTests.scala
  23. +246 −0 test/files/jvm/future-spec/PromiseTests.scala
  24. +130 −0 test/files/jvm/future-spec/TryTests.scala
  25. +110 −0 test/files/jvm/future-spec/main.scala
  26. +1,036 −0 test/files/jvm/scala-concurrent-tck.scala
View
@@ -288,7 +288,8 @@ INITIALISATION
This is to facilitate testing new command line options which do not yet exist in starr. -->
<property name="scalac.args.quickonly" value=""/>
<property name="scalac.args.all" value="${scalac.args} ${scalac.args.optimise}"/>
- <property name="scalac.args.quick" value="${scalac.args.all} ${scalac.args.quickonly}"/>
+ <property name="scalac.args.quick" value="${scalac.args.all} ${scalac.args.quickonly}"/>
+ <property name="scalac.args.strap.lib" value="${scalac.args.all} -Ydependent-method-types"/>
<!-- Setting-up Ant contrib tasks -->
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant/ant-contrib.jar"/>
<!-- This is the start time for the distribution -->
@@ -376,8 +377,9 @@ LOCAL REFERENCE BUILD (LOCKER)
<javac
srcdir="${src.dir}/library"
destdir="${build-locker.dir}/classes/library"
- classpath="${build-locker.dir}/classes/library"
+ classpathref="quick.compilation.path"
includes="**/*.java"
+ excludes="scala/concurrent/**/*.java"
target="1.5" source="1.5">
<compilerarg line="${javac.args}"/>
</javac>
@@ -392,6 +394,17 @@ LOCAL REFERENCE BUILD (LOCKER)
srcdir="${src.dir}/library"
jvmargs="${scalacfork.jvmargs}">
<include name="**/*.scala"/>
+ <!-- Exclude duration package and everything that depends on it -->
+ <exclude name="scala/concurrent/duration/**/*.scala"/>
+ <exclude name="scala/concurrent/package.scala"/>
+ <exclude name="scala/concurrent/Awaitable.scala"/>
+ <exclude name="scala/concurrent/Future.scala"/>
+ <exclude name="scala/concurrent/Promise.scala"/>
+ <exclude name="scala/concurrent/ExecutionContext.scala"/>
+ <exclude name="scala/concurrent/BlockContext.scala"/>
+ <exclude name="scala/concurrent/impl/Future.scala"/>
+ <exclude name="scala/concurrent/impl/Promise.scala"/>
+ <exclude name="scala/concurrent/impl/ExecutionContextImpl.scala"/>
<compilationpath>
<pathelement location="${build-locker.dir}/classes/library"/>
<pathelement location="${lib.dir}/forkjoin.jar"/>
@@ -546,7 +559,7 @@ QUICK BUILD (QUICK)
<javac
srcdir="${src.dir}/library"
destdir="${build-quick.dir}/classes/library"
- classpath="${build-quick.dir}/classes/library"
+ classpathref="quick.compilation.path"
includes="**/*.java"
target="1.5" source="1.5">
<compilerarg line="${javac.args}"/>
@@ -563,7 +576,7 @@ QUICK BUILD (QUICK)
destdir="${build-quick.dir}/classes/library"
compilerpathref="locker.classpath"
srcpath="${src.dir}/library"
- params="${scalac.args.quick}"
+ params="${scalac.args.quick} -Ydependent-method-types"
srcdir="${src.dir}/library"
jvmargs="${scalacfork.jvmargs}">
<include name="**/*.scala"/>
@@ -1083,7 +1096,7 @@ BOOTSTRAPPING BUILD (STRAP)
<javac
srcdir="${src.dir}/library"
destdir="${build-strap.dir}/classes/library"
- classpath="${build-strap.dir}/classes/library"
+ classpathref="strap.compilation.path"
includes="**/*.java"
target="1.5" source="1.5">
<compilerarg line="${javac.args}"/>
@@ -1100,7 +1113,7 @@ BOOTSTRAPPING BUILD (STRAP)
destdir="${build-strap.dir}/classes/library"
compilerpathref="pack.classpath"
srcpath="${src.dir}/library"
- params="${scalac.args.all}"
+ params="${scalac.args.strap.lib}"
srcdir="${src.dir}/library"
jvmargs="${scalacfork.jvmargs}">
<include name="**/*.scala"/>
@@ -1531,6 +1544,7 @@ DOCUMENTATION
docUncompilable="${src.dir}/library-aux"
sourcepath="${src.dir}"
classpathref="pack.classpath"
+ addparams="-Ydependent-method-types"
docRootContent="${build-docs.dir}/library/lib/rootdoc.txt">
<src>
<files includes="${src.dir}/actors"/>
@@ -0,0 +1,64 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+
+
+import scala.concurrent.duration.Duration
+
+
+
+/**
+ * An object that may eventually be completed with a result value of type `T` which may be
+ * awaited using blocking methods.
+ *
+ * The [[Await]] object provides methods that allow accessing the result of an `Awaitable`
+ * by blocking the current thread until the `Awaitable` has been completed or a timeout has
+ * occurred.
+ */
+trait Awaitable[+T] {
+
+ /**
+ * Await the "completed" state of this `Awaitable`.
+ *
+ * '''''This method should not be called directly; use [[Await.ready]] instead.'''''
+ *
+ * @param atMost
+ * maximum wait time, which may be negative (no waiting is done),
+ * [[scala.concurrent.duration.Duration.Inf Duration.Inf]] for unbounded waiting, or a finite positive
+ * duration
+ * @return this `Awaitable`
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ * @throws TimeoutException if after waiting for the specified time this `Awaitable` is still not ready
+ * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]]
+ */
+ @throws(classOf[TimeoutException])
+ @throws(classOf[InterruptedException])
+ def ready(atMost: Duration)(implicit permit: CanAwait): this.type
+
+ /**
+ * Await and return the result (of type `T`) of this `Awaitable`.
+ *
+ * '''''This method should not be called directly; use [[Await.result]] instead.'''''
+ *
+ * @param atMost
+ * maximum wait time, which may be negative (no waiting is done),
+ * [[scala.concurrent.duration.Duration.Inf Duration.Inf]] for unbounded waiting, or a finite positive
+ * duration
+ * @return the result value if the `Awaitable` is completed within the specific maximum wait time
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ * @throws TimeoutException if after waiting for the specified time this `Awaitable` is still not ready
+ * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]]
+ */
+ @throws(classOf[Exception])
+ def result(atMost: Duration)(implicit permit: CanAwait): T
+}
+
+
+
@@ -0,0 +1,77 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+/**
+ * A context to be notified by `scala.concurrent.blocking` when
+ * a thread is about to block. In effect this trait provides
+ * the implementation for `scala.concurrent.Await`.
+ * `scala.concurrent.Await.result()` and `scala.concurrent.Await.ready()`
+ * locates an instance of `BlockContext` by first looking for one
+ * provided through `BlockContext.withBlockContext()` and failing that,
+ * checking whether `Thread.currentThread` is an instance of `BlockContext`.
+ * So a thread pool can have its `java.lang.Thread` instances implement
+ * `BlockContext`. There's a default `BlockContext` used if the thread
+ * doesn't implement `BlockContext`.
+ *
+ * Typically, you'll want to chain to the previous `BlockContext`,
+ * like this:
+ * {{{
+ * val oldContext = BlockContext.current
+ * val myContext = new BlockContext {
+ * override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = {
+ * // you'd have code here doing whatever you need to do
+ * // when the thread is about to block.
+ * // Then you'd chain to the previous context:
+ * oldContext.blockOn(thunk)
+ * }
+ * }
+ * BlockContext.withBlockContext(myContext) {
+ * // then this block runs with myContext as the handler
+ * // for scala.concurrent.blocking
+ * }
+ * }}}
+ */
+trait BlockContext {
+
+ /** Used internally by the framework;
+ * Designates (and eventually executes) a thunk which potentially blocks the calling `Thread`.
+ *
+ * Clients must use `scala.concurrent.blocking` or `scala.concurrent.Await` instead.
+ */
+ def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T
+}
+
+object BlockContext {
+ private object DefaultBlockContext extends BlockContext {
+ override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = thunk
+ }
+
+ private val contextLocal = new ThreadLocal[BlockContext]()
+
+ /** Obtain the current thread's current `BlockContext`. */
+ def current: BlockContext = contextLocal.get match {
+ case null => Thread.currentThread match {
+ case ctx: BlockContext => ctx
+ case _ => DefaultBlockContext
+ }
+ case some => some
+ }
+
+ /** Pushes a current `BlockContext` while executing `body`. */
+ def withBlockContext[T](blockContext: BlockContext)(body: => T): T = {
+ val old = contextLocal.get // can be null
+ try {
+ contextLocal.set(blockContext)
+ body
+ } finally {
+ contextLocal.set(old)
+ }
+ }
+}
@@ -9,8 +9,6 @@
package scala.concurrent
-import ops.future
-
/** A <code>DelayedLazyVal</code> is a wrapper for lengthy
* computations which have a valid partially computed result.
* The first argument is a function for obtaining the result
@@ -41,7 +39,7 @@ class DelayedLazyVal[T](f: () => T, body: => Unit) {
*/
def apply(): T = if (isDone) complete else f()
- future {
+ ops.future {
body
_isDone = true
}
@@ -0,0 +1,88 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+
+import java.util.concurrent.{ Executor, ExecutorService }
+import scala.annotation.implicitNotFound
+
+/**
+ * An `ExecutionContext` is an abstraction over an entity that can execute program logic.
+ */
+@implicitNotFound("Cannot find an implicit ExecutionContext, either require one yourself or import ExecutionContext.Implicits.global")
+trait ExecutionContext {
+
+ /** Runs a block of code on this execution context.
+ */
+ def execute(runnable: Runnable): Unit
+
+ /** Reports that an asynchronous computation failed.
+ */
+ def reportFailure(t: Throwable): Unit
+
+ /** Prepares for the execution of a task. Returns the prepared
+ * execution context. A valid implementation of `prepare` is one
+ * that simply returns `this`.
+ */
+ def prepare(): ExecutionContext = this
+
+}
+
+/**
+ * Union interface since Java does not support union types
+ */
+trait ExecutionContextExecutor extends ExecutionContext with Executor
+
+/**
+ * Union interface since Java does not support union types
+ */
+trait ExecutionContextExecutorService extends ExecutionContextExecutor with ExecutorService
+
+
+/** Contains factory methods for creating execution contexts.
+ */
+object ExecutionContext {
+ /**
+ * This is the explicit global ExecutionContext,
+ * call this when you want to provide the global ExecutionContext explicitly
+ */
+ def global: ExecutionContextExecutor = Implicits.global
+
+ object Implicits {
+ /**
+ * This is the implicit global ExecutionContext,
+ * import this when you want to provide the global ExecutionContext implicitly
+ */
+ implicit lazy val global: ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(null: Executor)
+ }
+
+ /** Creates an `ExecutionContext` from the given `ExecutorService`.
+ */
+ def fromExecutorService(e: ExecutorService, reporter: Throwable => Unit): ExecutionContextExecutorService =
+ impl.ExecutionContextImpl.fromExecutorService(e, reporter)
+
+ /** Creates an `ExecutionContext` from the given `ExecutorService` with the default Reporter.
+ */
+ def fromExecutorService(e: ExecutorService): ExecutionContextExecutorService = fromExecutorService(e, defaultReporter)
+
+ /** Creates an `ExecutionContext` from the given `Executor`.
+ */
+ def fromExecutor(e: Executor, reporter: Throwable => Unit): ExecutionContextExecutor =
+ impl.ExecutionContextImpl.fromExecutor(e, reporter)
+
+ /** Creates an `ExecutionContext` from the given `Executor` with the default Reporter.
+ */
+ def fromExecutor(e: Executor): ExecutionContextExecutor = fromExecutor(e, defaultReporter)
+
+ /** The default reporter simply prints the stack trace of the `Throwable` to System.err.
+ */
+ def defaultReporter: Throwable => Unit = (t: Throwable) => t.printStackTrace()
+}
+
+
Oops, something went wrong.

0 comments on commit 61862d2

Please sign in to comment.