From d38ca89f8d50c0d6eafac623f0edc165c01f8bd4 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Mon, 5 Dec 2011 14:33:42 +0100 Subject: Migration of scala.concurrent to clean fork of new Scala repo. --- .../scala/concurrent/AbstractPromise.java.disabled | 17 + src/library/scala/concurrent/Blockable.scala | 23 + .../scala/concurrent/ExecutionContext.scala | 117 +++++ src/library/scala/concurrent/Future.scala | 214 +++++++++ src/library/scala/concurrent/Promise.scala | 30 ++ src/library/scala/concurrent/package.scala | 49 +++ src/library/scala/util/Duration.scala | 485 +++++++++++++++++++++ src/library/scala/util/Timeout.scala | 33 ++ 8 files changed, 968 insertions(+) create mode 100644 src/library/scala/concurrent/AbstractPromise.java.disabled create mode 100644 src/library/scala/concurrent/Blockable.scala create mode 100644 src/library/scala/concurrent/ExecutionContext.scala create mode 100644 src/library/scala/concurrent/Future.scala create mode 100644 src/library/scala/concurrent/Promise.scala create mode 100644 src/library/scala/concurrent/package.scala create mode 100644 src/library/scala/util/Duration.scala create mode 100644 src/library/scala/util/Timeout.scala (limited to 'src') diff --git a/src/library/scala/concurrent/AbstractPromise.java.disabled b/src/library/scala/concurrent/AbstractPromise.java.disabled new file mode 100644 index 0000000000..726e6a3156 --- /dev/null +++ b/src/library/scala/concurrent/AbstractPromise.java.disabled @@ -0,0 +1,17 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ + +package scala.concurrent; +/* +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import scala.concurrent.forkjoin.*; + +abstract class AbstractPromise { + //private volatile Object _ref = DefaultPromise.EmptyPending(); + protected final static AtomicReferenceFieldUpdater updater = + AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); + + protected void compute() { } +} +*/ diff --git a/src/library/scala/concurrent/Blockable.scala b/src/library/scala/concurrent/Blockable.scala new file mode 100644 index 0000000000..1ad02c7469 --- /dev/null +++ b/src/library/scala/concurrent/Blockable.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent + + + +import scala.annotation.implicitNotFound + + + +trait Blockable[+T] { + @implicitNotFound(msg = "Blocking must be done by calling `block on b`, where `b` is the Blockable object.") + def block()(implicit canblock: CanBlock): T +} + + + diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala new file mode 100644 index 0000000000..972a76a95a --- /dev/null +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -0,0 +1,117 @@ +package scala.concurrent + +import java.util.concurrent.{ Executors, Future => JFuture } +import scala.util.{ Duration, Timeout } +import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } + +trait ExecutionContext { + + protected implicit object CanBlockEvidence extends CanBlock + + def execute(task: Runnable): Unit + + def makeTask[T](task: () => T)(implicit timeout: Timeout): Task[T] + + def makePromise[T](timeout: Timeout): Promise[T] + + def blockingCall[T](body: Blockable[T]): T + +} + +trait Task[T] { + + def start(): Unit + def future: Future[T] + +} + +/* DONE: The challenge is to make ForkJoinPromise inherit from RecursiveAction + * to avoid an object allocation per promise. This requires turning DefaultPromise + * into a trait, i.e., removing its constructor parameters. + */ +private[concurrent] class ForkJoinTaskImpl[T](context: ForkJoinExecutionContext, body: () => T, within: Timeout) extends FJTask[T] with Task[T] { + + val timeout = within + implicit val dispatcher = context + + // body of RecursiveTask + def compute(): T = + body() + + def start(): Unit = + fork() + + def future: Future[T] = { + null + } + + // TODO FIXME: handle timeouts + def await(atMost: Duration): this.type = + await + + def await: this.type = { + this.join() + this + } + + def tryCancel(): Unit = + tryUnfork() +} + +private[concurrent] final class ForkJoinExecutionContext extends ExecutionContext { + val pool = new ForkJoinPool + + @inline + private def executeForkJoinTask(task: RecursiveAction) { + if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) + task.fork() + else + pool execute task + } + + def execute(task: Runnable) { + val action = new RecursiveAction { def compute() { task.run() } } + executeForkJoinTask(action) + } + + def makeTask[T](body: () => T)(implicit timeout: Timeout): Task[T] = { + new ForkJoinTaskImpl(this, body, timeout) + } + + def makePromise[T](timeout: Timeout): Promise[T] = + null + + def blockingCall[T](body: Blockable[T]): T = + body.block()(CanBlockEvidence) + +} + +/** + * Implements a blocking execution context + */ +/* +private[concurrent] class BlockingExecutionContext extends ExecutionContext { + //val pool = makeCachedThreadPool // TODO FIXME: need to merge thread pool factory methods from Heather's parcolls repo + + def execute(task: Runnable) { + /* TODO + val p = newPromise(task.run()) + p.start() + pool execute p + */ + } + + // TODO FIXME: implement + def newPromise[T](body: => T): Promise[T] = { + throw new Exception("not yet implemented") + } +} +*/ + +object ExecutionContext { + + lazy val forNonBlocking = new ForkJoinExecutionContext + + //lazy val forBlocking = new BlockingExecutionContext + +} diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala new file mode 100644 index 0000000000..222c5d18b6 --- /dev/null +++ b/src/library/scala/concurrent/Future.scala @@ -0,0 +1,214 @@ + +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ + +package scala.concurrent + +//import akka.AkkaException (replaced with Exception) +//import akka.event.Logging.Error (removed all logging) +import scala.util.{ Timeout, Duration } +import scala.Option +//import akka.japi.{ Procedure, Function ⇒ JFunc, Option ⇒ JOption } (commented methods) + +import java.util.concurrent.{ ConcurrentLinkedQueue, TimeUnit, Callable } +import java.util.concurrent.TimeUnit.{ NANOSECONDS ⇒ NANOS, MILLISECONDS ⇒ MILLIS } +import java.lang.{ Iterable ⇒ JIterable } +import java.util.{ LinkedList ⇒ JLinkedList } + +import scala.annotation.tailrec +import scala.collection.mutable.Stack +//import akka.util.Switch (commented method) +import java.{ lang ⇒ jl } +import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } + + + +/** The trait that represents futures. + * + * @define futureTimeout + * The timeout of the future is: + * - if this future was obtained from a task (i.e. by calling `task.future`), the timeout associated with that task + * - if this future was obtained from a promise (i.e. by calling `promise.future`), the timeout associated with that promise + * - if this future was obtained from a combinator on some other future `g` (e.g. by calling `g.map(_)`), the timeout of `g` + * - if this future was obtained from a combinator on multiple futures `g0`, ..., `g1`, the minimum of the timeouts of these futures + * + * @define multipleCallbacks + * Multiple callbacks may be registered; there is no guarantee that they will be + * executed in a particular order. + * + * @define caughtThrowables + * The future may contain a throwable object and this means that the future failed. + * Futures obtained through combinators have the same exception as the future they were obtained from. + * The following throwable objects are treated differently: + * - Error - errors are not contained within futures + * - NonLocalControlException - not contained within futures + * - InterruptedException - not contained within futures + * + * @define forComprehensionExamples + * Example: + * + * {{{ + * val f = future { 5 } + * val g = future { 3 } + * val h = for { + * x: Int <- f // returns Future(5) + * y: Int <- g // returns Future(5) + * } yield x + y + * }}} + * + * is translated to: + * + * {{{ + * f flatMap { (x: Int) => g map { (y: Int) => x + y } } + * }}} + */ +trait Future[+T] extends Blockable[T] { + + /* Callbacks */ + + /** When this future is completed successfully (i.e. with a value), + * apply the provided function to the value. + * + * If the future has already been completed with a value, + * this will either be applied immediately or be scheduled asynchronously. + * + * Will not be called in case of a timeout. + * + * Will not be called in case of an exception. + * + * $multipleCallbacks + */ + def onSuccess[U](func: T => U): this.type + + /** When this future is completed with a failure (i.e. with a throwable), + * apply the provided function to the throwable. + * + * $caughtThrowables + * + * If the future has already been completed with a failure, + * this will either be applied immediately or be scheduled asynchronously. + * + * Will not be called in case of a timeout. + * + * Will not be called in case of an exception. + * + * $multipleCallbacks + */ + def onFailure[U](func: Throwable => U): this.type + + /** When this future times out, apply the provided function. + * + * If the future has already timed out, + * this will either be applied immediately or be scheduled asynchronously. + * + * $multipleCallbacks + */ + def onTimeout[U](func: => U): this.type + + /** When this future is completed, either through an exception, a timeout, or a value, + * apply the provided function. + * + * If the future has already been completed, + * this will either be applied immediately or be scheduled asynchronously. + * + * $multipleCallbacks + */ + def onComplete[U](func: Either[Throwable, T] => U): this.type + + + /* Various info */ + + /** Tests whether this Future's timeout has expired. + * + * $futureTimeout + * + * Note that an expired Future may still contain a value, or it may be + * completed with a value. + */ + def isTimedout: Boolean + + /** This future's timeout. + * + * $futureTimeout + */ + def timeout: Timeout + + /** This future's timeout in nanoseconds. + * + * $futureTimeout + */ + def timeoutInNanos = if (timeout.duration.isFinite) timeout.duration.toNanos else Long.MaxValue + + + /* Projections */ + + def failed: Future[Exception] + + def timedout: Future[Timeout] + + + /* Monadic operations */ + + /** Creates a new future that will handle any matching throwable that this + * future might contain. If there is no match, or if this future contains + * a valid result then the new future will contain the same. + * + * Example: + * + * {{{ + * future (6 / 0) recover { case e: ArithmeticException ⇒ 0 } // result: 0 + * future (6 / 0) recover { case e: NotFoundException ⇒ 0 } // result: exception + * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3 + * }}} + */ + def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit timeout: Timeout): Future[U] + + /** Asynchronously processes the value in the future once the value becomes available. + * + * Will not be called if the future times out or fails. + * + * This method typically registers an `onResult` callback. + */ + def foreach[U](f: T => U): Unit + + /** Creates a new future by applying a function to the successful result of + * this future. If this future is completed with an exception then the new + * future will also contain this exception. + * + * $forComprehensionExample + */ + def map[S](f: T => S)(implicit timeout: Timeout): Future[S] + + /** Creates a new future by applying a function to the successful result of + * this future, and returns the result of the function as the new future. + * If this future is completed with an exception then the new future will + * also contain this exception. + * + * $forComprehensionExample + */ + def flatMap[S](f: T => Future[S])(implicit timeout: Timeout): Future[S] + + /** Creates a new future by filtering the value of the current future with a predicate. + * + * If the current future contains a value which satisfies the predicate, the new future will also hold that value. + * Otherwise, the resulting future will fail with a `NoSuchElementException`. + * + * If the current future fails or times out, the resulting future also fails or times out, respectively. + * + * Example: + * {{{ + * val f = future { 5 } + * val g = g filter { _ % 2 == 1 } + * val h = f filter { _ % 2 == 0 } + * block on g // evaluates to 5 + * block on h // throw a NoSuchElementException + * }}} + */ + def filter(p: T => Boolean)(implicit timeout: Timeout): Future[T] + +} + +class FutureTimeoutException(message: String, cause: Throwable = null) extends Exception(message, cause) { + def this(message: String) = this(message, null) +} diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala new file mode 100644 index 0000000000..109f6b4eba --- /dev/null +++ b/src/library/scala/concurrent/Promise.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent + +trait Promise[T] { + def future: Future[T] + + def fulfill(value: T): Unit + def fail(t: Throwable): Unit +} + +object Promise { + /* + /** + * Creates a non-completed, new, Promise with the supplied timeout in milliseconds + */ + def apply[A](timeout: Timeout)(implicit dispatcher: MessageDispatcher): Promise[A] = DefaultPromise[A](timeout) + + /** + * Creates a non-completed, new, Promise with the default timeout (akka.actor.timeout in conf) + */ + def apply[A]()(implicit dispatcher: MessageDispatcher, timeout: Timeout): Promise[A] = apply(timeout) + */ +} diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala new file mode 100644 index 0000000000..2a1ff9b799 --- /dev/null +++ b/src/library/scala/concurrent/package.scala @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + + +package scala + + + + +/** This package object contains primitives for parallel programming. + */ +package object concurrent { + + type MessageDispatcher = ExecutionContext // TODO FIXME: change futures to use execution context + + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { + override protected def initialValue = null + } + + object block { + def on[T](body: =>T): T = on(new Blockable[T] { + def block()(implicit cb: CanBlock) = body + }) + + def on[T](blockable: Blockable[T]): T = { + currentExecutionContext.get match { + case null => blockable.block()(null) // outside + case x => x.blockingCall(blockable) // inside an execution context thread + } + } + } + + + def future[T](body: =>T): Future[T] = null // TODO + +} + + +package concurrent { + + private[concurrent] trait CanBlock + +} diff --git a/src/library/scala/util/Duration.scala b/src/library/scala/util/Duration.scala new file mode 100644 index 0000000000..4c118f8b3b --- /dev/null +++ b/src/library/scala/util/Duration.scala @@ -0,0 +1,485 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ + +package scala.util + +import java.util.concurrent.TimeUnit +import TimeUnit._ +import java.lang.{ Long ⇒ JLong, Double ⇒ JDouble } +//import akka.actor.ActorSystem (commented methods) + +class TimerException(message: String) extends RuntimeException(message) + +/** + * Simple timer class. + * Usage: + *
+ *   import akka.util.duration._
+ *   import akka.util.Timer
+ *
+ *   val timer = Timer(30.seconds)
+ *   while (timer.isTicking) { ... }
+ * 
+ */ +case class Timer(duration: Duration, throwExceptionOnTimeout: Boolean = false) { + val startTimeInMillis = System.currentTimeMillis + val timeoutInMillis = duration.toMillis + + /** + * Returns true while the timer is ticking. After that it either throws and exception or + * returns false. Depending on if the 'throwExceptionOnTimeout' argument is true or false. + */ + def isTicking: Boolean = { + if (!(timeoutInMillis > (System.currentTimeMillis - startTimeInMillis))) { + if (throwExceptionOnTimeout) throw new TimerException("Time out after " + duration) + else false + } else true + } +} + +object Duration { + def apply(length: Long, unit: TimeUnit): Duration = new FiniteDuration(length, unit) + def apply(length: Double, unit: TimeUnit): Duration = fromNanos(unit.toNanos(1) * length) + def apply(length: Long, unit: String): Duration = new FiniteDuration(length, timeUnit(unit)) + + def fromNanos(nanos: Long): Duration = { + if (nanos % 86400000000000L == 0) { + Duration(nanos / 86400000000000L, DAYS) + } else if (nanos % 3600000000000L == 0) { + Duration(nanos / 3600000000000L, HOURS) + } else if (nanos % 60000000000L == 0) { + Duration(nanos / 60000000000L, MINUTES) + } else if (nanos % 1000000000L == 0) { + Duration(nanos / 1000000000L, SECONDS) + } else if (nanos % 1000000L == 0) { + Duration(nanos / 1000000L, MILLISECONDS) + } else if (nanos % 1000L == 0) { + Duration(nanos / 1000L, MICROSECONDS) + } else { + Duration(nanos, NANOSECONDS) + } + } + + def fromNanos(nanos: Double): Duration = fromNanos((nanos + 0.5).asInstanceOf[Long]) + + /** + * Construct a Duration by parsing a String. In case of a format error, a + * RuntimeException is thrown. See `unapply(String)` for more information. + */ + def apply(s: String): Duration = unapply(s) getOrElse sys.error("format error") + + /** + * Deconstruct a Duration into length and unit if it is finite. + */ + def unapply(d: Duration): Option[(Long, TimeUnit)] = { + if (d.finite_?) { + Some((d.length, d.unit)) + } else { + None + } + } + + private val RE = ("""^\s*(\d+(?:\.\d+)?)\s*""" + // length part + "(?:" + // units are distinguished in separate match groups + "(d|day|days)|" + + "(h|hour|hours)|" + + "(min|minute|minutes)|" + + "(s|sec|second|seconds)|" + + "(ms|milli|millis|millisecond|milliseconds)|" + + "(µs|micro|micros|microsecond|microseconds)|" + + "(ns|nano|nanos|nanosecond|nanoseconds)" + + """)\s*$""").r // close the non-capturing group + private val REinf = """^\s*Inf\s*$""".r + private val REminf = """^\s*(?:-\s*|Minus)Inf\s*""".r + + /** + * Parse String, return None if no match. Format is `""`, where + * whitespace is allowed before, between and after the parts. Infinities are + * designated by `"Inf"` and `"-Inf"` or `"MinusInf"`. + */ + def unapply(s: String): Option[Duration] = s match { + case RE(length, d, h, m, s, ms, mus, ns) ⇒ + if (d ne null) Some(Duration(JDouble.parseDouble(length), DAYS)) else if (h ne null) Some(Duration(JDouble.parseDouble(length), HOURS)) else if (m ne null) Some(Duration(JDouble.parseDouble(length), MINUTES)) else if (s ne null) Some(Duration(JDouble.parseDouble(length), SECONDS)) else if (ms ne null) Some(Duration(JDouble.parseDouble(length), MILLISECONDS)) else if (mus ne null) Some(Duration(JDouble.parseDouble(length), MICROSECONDS)) else if (ns ne null) Some(Duration(JDouble.parseDouble(length), NANOSECONDS)) else + sys.error("made some error in regex (should not be possible)") + case REinf() ⇒ Some(Inf) + case REminf() ⇒ Some(MinusInf) + case _ ⇒ None + } + + /** + * Parse TimeUnit from string representation. + */ + def timeUnit(unit: String) = unit.toLowerCase match { + case "d" | "day" | "days" ⇒ DAYS + case "h" | "hour" | "hours" ⇒ HOURS + case "min" | "minute" | "minutes" ⇒ MINUTES + case "s" | "sec" | "second" | "seconds" ⇒ SECONDS + case "ms" | "milli" | "millis" | "millisecond" | "milliseconds" ⇒ MILLISECONDS + case "µs" | "micro" | "micros" | "microsecond" | "microseconds" ⇒ MICROSECONDS + case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" ⇒ NANOSECONDS + } + + val Zero: Duration = new FiniteDuration(0, NANOSECONDS) + val Undefined: Duration = new Duration with Infinite { + override def toString = "Duration.Undefined" + override def equals(other: Any) = other.asInstanceOf[AnyRef] eq this + override def +(other: Duration): Duration = throw new IllegalArgumentException("cannot add Undefined duration") + override def -(other: Duration): Duration = throw new IllegalArgumentException("cannot subtract Undefined duration") + override def *(factor: Double): Duration = throw new IllegalArgumentException("cannot multiply Undefined duration") + override def /(factor: Double): Duration = throw new IllegalArgumentException("cannot divide Undefined duration") + override def /(other: Duration): Double = throw new IllegalArgumentException("cannot divide Undefined duration") + def >(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration") + def >=(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration") + def <(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration") + def <=(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration") + def unary_- : Duration = throw new IllegalArgumentException("cannot negate Undefined duration") + } + + trait Infinite { + this: Duration ⇒ + + override def equals(other: Any) = false + + def +(other: Duration): Duration = + other match { + case _: this.type ⇒ this + case _: Infinite ⇒ throw new IllegalArgumentException("illegal addition of infinities") + case _ ⇒ this + } + def -(other: Duration): Duration = + other match { + case _: this.type ⇒ throw new IllegalArgumentException("illegal subtraction of infinities") + case _ ⇒ this + } + def *(factor: Double): Duration = this + def /(factor: Double): Duration = this + def /(other: Duration): Double = + other match { + case _: Infinite ⇒ throw new IllegalArgumentException("illegal division of infinities") + // maybe questionable but pragmatic: Inf / 0 => Inf + case x ⇒ Double.PositiveInfinity * (if ((this > Zero) ^ (other >= Zero)) -1 else 1) + } + + def finite_? = false + + def length: Long = throw new IllegalArgumentException("length not allowed on infinite Durations") + def unit: TimeUnit = throw new IllegalArgumentException("unit not allowed on infinite Durations") + def toNanos: Long = throw new IllegalArgumentException("toNanos not allowed on infinite Durations") + def toMicros: Long = throw new IllegalArgumentException("toMicros not allowed on infinite Durations") + def toMillis: Long = throw new IllegalArgumentException("toMillis not allowed on infinite Durations") + def toSeconds: Long = throw new IllegalArgumentException("toSeconds not allowed on infinite Durations") + def toMinutes: Long = throw new IllegalArgumentException("toMinutes not allowed on infinite Durations") + def toHours: Long = throw new IllegalArgumentException("toHours not allowed on infinite Durations") + def toDays: Long = throw new IllegalArgumentException("toDays not allowed on infinite Durations") + def toUnit(unit: TimeUnit): Double = throw new IllegalArgumentException("toUnit not allowed on infinite Durations") + + def printHMS = toString + } + + /** + * Infinite duration: greater than any other and not equal to any other, + * including itself. + */ + val Inf: Duration = new Duration with Infinite { + override def toString = "Duration.Inf" + def >(other: Duration) = true + def >=(other: Duration) = true + def <(other: Duration) = false + def <=(other: Duration) = false + def unary_- : Duration = MinusInf + } + + /** + * Infinite negative duration: lesser than any other and not equal to any other, + * including itself. + */ + val MinusInf: Duration = new Duration with Infinite { + override def toString = "Duration.MinusInf" + def >(other: Duration) = false + def >=(other: Duration) = false + def <(other: Duration) = true + def <=(other: Duration) = true + def unary_- : Duration = Inf + } + + // Java Factories + def create(length: Long, unit: TimeUnit): Duration = apply(length, unit) + def create(length: Double, unit: TimeUnit): Duration = apply(length, unit) + def create(length: Long, unit: String): Duration = apply(length, unit) + def parse(s: String): Duration = unapply(s).get +} + +/** + * Utility for working with java.util.concurrent.TimeUnit durations. + * + *

+ * Examples of usage from Java: + *

+ * import akka.util.FiniteDuration;
+ * import java.util.concurrent.TimeUnit;
+ *
+ * Duration duration = new FiniteDuration(100, MILLISECONDS);
+ * Duration duration = new FiniteDuration(5, "seconds");
+ *
+ * duration.toNanos();
+ * 
+ * + *

+ * Examples of usage from Scala: + *

+ * import akka.util.Duration
+ * import java.util.concurrent.TimeUnit
+ *
+ * val duration = Duration(100, MILLISECONDS)
+ * val duration = Duration(100, "millis")
+ *
+ * duration.toNanos
+ * duration < 1.second
+ * duration <= Duration.Inf
+ * 
+ * + *

+ * Implicits are also provided for Int, Long and Double. Example usage: + *

+ * import akka.util.duration._
+ *
+ * val duration = 100 millis
+ * 
+ * + * Extractors, parsing and arithmetic are also included: + *
+ * val d = Duration("1.2 µs")
+ * val Duration(length, unit) = 5 millis
+ * val d2 = d * 2.5
+ * val d3 = d2 + 1.millisecond
+ * 
+ */ +abstract class Duration extends Serializable { + def length: Long + def unit: TimeUnit + def toNanos: Long + def toMicros: Long + def toMillis: Long + def toSeconds: Long + def toMinutes: Long + def toHours: Long + def toDays: Long + def toUnit(unit: TimeUnit): Double + def printHMS: String + def <(other: Duration): Boolean + def <=(other: Duration): Boolean + def >(other: Duration): Boolean + def >=(other: Duration): Boolean + def +(other: Duration): Duration + def -(other: Duration): Duration + def *(factor: Double): Duration + def /(factor: Double): Duration + def /(other: Duration): Double + def unary_- : Duration + def finite_? : Boolean +// def dilated(implicit system: ActorSystem): Duration = this * system.settings.TestTimeFactor + def min(other: Duration): Duration = if (this < other) this else other + def max(other: Duration): Duration = if (this > other) this else other + def sleep(): Unit = Thread.sleep(toMillis) + + // Java API + def lt(other: Duration) = this < other + def lteq(other: Duration) = this <= other + def gt(other: Duration) = this > other + def gteq(other: Duration) = this >= other + def plus(other: Duration) = this + other + def minus(other: Duration) = this - other + def mul(factor: Double) = this * factor + def div(factor: Double) = this / factor + def div(other: Duration) = this / other + def neg() = -this + def isFinite() = finite_? +} + +class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { + import Duration._ + + def this(length: Long, unit: String) = this(length, Duration.timeUnit(unit)) + + def toNanos = unit.toNanos(length) + def toMicros = unit.toMicros(length) + def toMillis = unit.toMillis(length) + def toSeconds = unit.toSeconds(length) + def toMinutes = unit.toMinutes(length) + def toHours = unit.toHours(length) + def toDays = unit.toDays(length) + def toUnit(u: TimeUnit) = long2double(toNanos) / NANOSECONDS.convert(1, u) + + override def toString = this match { + case Duration(1, DAYS) ⇒ "1 day" + case Duration(x, DAYS) ⇒ x + " days" + case Duration(1, HOURS) ⇒ "1 hour" + case Duration(x, HOURS) ⇒ x + " hours" + case Duration(1, MINUTES) ⇒ "1 minute" + case Duration(x, MINUTES) ⇒ x + " minutes" + case Duration(1, SECONDS) ⇒ "1 second" + case Duration(x, SECONDS) ⇒ x + " seconds" + case Duration(1, MILLISECONDS) ⇒ "1 millisecond" + case Duration(x, MILLISECONDS) ⇒ x + " milliseconds" + case Duration(1, MICROSECONDS) ⇒ "1 microsecond" + case Duration(x, MICROSECONDS) ⇒ x + " microseconds" + case Duration(1, NANOSECONDS) ⇒ "1 nanosecond" + case Duration(x, NANOSECONDS) ⇒ x + " nanoseconds" + } + + def printHMS = "%02d:%02d:%06.3f".format(toHours, toMinutes % 60, toMillis / 1000.0 % 60) + + def <(other: Duration) = { + if (other.finite_?) { + toNanos < other.asInstanceOf[FiniteDuration].toNanos + } else { + other > this + } + } + + def <=(other: Duration) = { + if (other.finite_?) { + toNanos <= other.asInstanceOf[FiniteDuration].toNanos + } else { + other >= this + } + } + + def >(other: Duration) = { + if (other.finite_?) { + toNanos > other.asInstanceOf[FiniteDuration].toNanos + } else { + other < this + } + } + + def >=(other: Duration) = { + if (other.finite_?) { + toNanos >= other.asInstanceOf[FiniteDuration].toNanos + } else { + other <= this + } + } + + def +(other: Duration) = { + if (!other.finite_?) { + other + } else { + val nanos = toNanos + other.asInstanceOf[FiniteDuration].toNanos + fromNanos(nanos) + } + } + + def -(other: Duration) = { + if (!other.finite_?) { + other + } else { + val nanos = toNanos - other.asInstanceOf[FiniteDuration].toNanos + fromNanos(nanos) + } + } + + def *(factor: Double) = fromNanos(long2double(toNanos) * factor) + + def /(factor: Double) = fromNanos(long2double(toNanos) / factor) + + def /(other: Duration) = if (other.finite_?) long2double(toNanos) / other.toNanos else 0 + + def unary_- = Duration(-length, unit) + + def finite_? = true + + override def equals(other: Any) = + other.isInstanceOf[FiniteDuration] && + toNanos == other.asInstanceOf[FiniteDuration].toNanos + + override def hashCode = toNanos.asInstanceOf[Int] +} + +class DurationInt(n: Int) { + def nanoseconds = Duration(n, NANOSECONDS) + def nanos = Duration(n, NANOSECONDS) + def nanosecond = Duration(n, NANOSECONDS) + def nano = Duration(n, NANOSECONDS) + + def microseconds = Duration(n, MICROSECONDS) + def micros = Duration(n, MICROSECONDS) + def microsecond = Duration(n, MICROSECONDS) + def micro = Duration(n, MICROSECONDS) + + def milliseconds = Duration(n, MILLISECONDS) + def millis = Duration(n, MILLISECONDS) + def millisecond = Duration(n, MILLISECONDS) + def milli = Duration(n, MILLISECONDS) + + def seconds = Duration(n, SECONDS) + def second = Duration(n, SECONDS) + + def minutes = Duration(n, MINUTES) + def minute = Duration(n, MINUTES) + + def hours = Duration(n, HOURS) + def hour = Duration(n, HOURS) + + def days = Duration(n, DAYS) + def day = Duration(n, DAYS) +} + +class DurationLong(n: Long) { + def nanoseconds = Duration(n, NANOSECONDS) + def nanos = Duration(n, NANOSECONDS) + def nanosecond = Duration(n, NANOSECONDS) + def nano = Duration(n, NANOSECONDS) + + def microseconds = Duration(n, MICROSECONDS) + def micros = Duration(n, MICROSECONDS) + def microsecond = Duration(n, MICROSECONDS) + def micro = Duration(n, MICROSECONDS) + + def milliseconds = Duration(n, MILLISECONDS) + def millis = Duration(n, MILLISECONDS) + def millisecond = Duration(n, MILLISECONDS) + def milli = Duration(n, MILLISECONDS) + + def seconds = Duration(n, SECONDS) + def second = Duration(n, SECONDS) + + def minutes = Duration(n, MINUTES) + def minute = Duration(n, MINUTES) + + def hours = Duration(n, HOURS) + def hour = Duration(n, HOURS) + + def days = Duration(n, DAYS) + def day = Duration(n, DAYS) +} + +class DurationDouble(d: Double) { + def nanoseconds = Duration(d, NANOSECONDS) + def nanos = Duration(d, NANOSECONDS) + def nanosecond = Duration(d, NANOSECONDS) + def nano = Duration(d, NANOSECONDS) + + def microseconds = Duration(d, MICROSECONDS) + def micros = Duration(d, MICROSECONDS) + def microsecond = Duration(d, MICROSECONDS) + def micro = Duration(d, MICROSECONDS) + + def milliseconds = Duration(d, MILLISECONDS) + def millis = Duration(d, MILLISECONDS) + def millisecond = Duration(d, MILLISECONDS) + def milli = Duration(d, MILLISECONDS) + + def seconds = Duration(d, SECONDS) + def second = Duration(d, SECONDS) + + def minutes = Duration(d, MINUTES) + def minute = Duration(d, MINUTES) + + def hours = Duration(d, HOURS) + def hour = Duration(d, HOURS) + + def days = Duration(d, DAYS) + def day = Duration(d, DAYS) +} diff --git a/src/library/scala/util/Timeout.scala b/src/library/scala/util/Timeout.scala new file mode 100644 index 0000000000..0190675344 --- /dev/null +++ b/src/library/scala/util/Timeout.scala @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +package scala.util + +import java.util.concurrent.TimeUnit + +case class Timeout(duration: Duration) { + def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS)) + def this(length: Long, unit: TimeUnit) = this(Duration(length, unit)) +} + +object Timeout { + /** + * A timeout with zero duration, will cause most requests to always timeout. + */ + val zero = new Timeout(Duration.Zero) + + /** + * A Timeout with infinite duration. Will never timeout. Use extreme caution with this + * as it may cause memory leaks, blocked threads, or may not even be supported by + * the receiver, which would result in an exception. + */ + val never = new Timeout(Duration.Inf) + + def apply(timeout: Long) = new Timeout(timeout) + def apply(length: Long, unit: TimeUnit) = new Timeout(length, unit) + + implicit def durationToTimeout(duration: Duration) = new Timeout(duration) + implicit def intToTimeout(timeout: Int) = new Timeout(timeout) + implicit def longToTimeout(timeout: Long) = new Timeout(timeout) + //implicit def defaultTimeout(implicit system: ActorSystem) = system.settings.ActorTimeout (have to introduce this in ActorSystem) +} -- cgit v1.2.3 From 4f4fdf54d63eda31bd1fedde963111cff1eb5640 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Mon, 5 Dec 2011 16:16:30 +0100 Subject: Add Akka's future implementation for reference --- src/library/scala/concurrent/Future.scala.disabled | 1051 ++++++++++++++++++++ 1 file changed, 1051 insertions(+) create mode 100644 src/library/scala/concurrent/Future.scala.disabled (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala.disabled b/src/library/scala/concurrent/Future.scala.disabled new file mode 100644 index 0000000000..3cd9bbeb6e --- /dev/null +++ b/src/library/scala/concurrent/Future.scala.disabled @@ -0,0 +1,1051 @@ +/* +class FutureFactory(dispatcher: MessageDispatcher, timeout: Timeout) { + + // TODO: remove me ASAP !!! + implicit val _dispatcher = dispatcher + + /** + * Java API, equivalent to Future.apply + */ + def future[T](body: Callable[T]): Future[T] = + Future(body.call, timeout) + + /** + * Java API, equivalent to Future.apply + */ + def future[T](body: Callable[T], timeout: Timeout): Future[T] = + Future(body.call, timeout) + + /** + * Java API, equivalent to Future.apply + */ + def future[T](body: Callable[T], timeout: Long): Future[T] = + Future(body.call, timeout) + + /** + * Java API, equivalent to Future.apply + */ + def future[T](body: Callable[T], dispatcher: MessageDispatcher): Future[T] = + Future(body.call)(dispatcher, timeout) + + /** + * Java API, equivalent to Future.apply + */ + def future[T](body: Callable[T], timeout: Timeout, dispatcher: MessageDispatcher): Future[T] = + Future(body.call)(dispatcher, timeout) + + /** + * Java API, equivalent to Future.apply + */ + def future[T](body: Callable[T], timeout: Long, dispatcher: MessageDispatcher): Future[T] = + Future(body.call)(dispatcher, timeout) + + /** + * Java API. + * Returns a Future that will hold the optional result of the first Future with a result that matches the predicate + */ +/* + def find[T <: AnyRef](futures: JIterable[Future[T]], predicate: JFunc[T, java.lang.Boolean], timeout: Timeout): Future[JOption[T]] = { + val pred: T ⇒ Boolean = predicate.apply(_) + Future.find[T]((scala.collection.JavaConversions.iterableAsScalaIterable(futures)), timeout)(pred).map(JOption.fromScalaOption(_))(timeout) + } + + def find[T <: AnyRef](futures: JIterable[Future[T]], predicate: JFunc[T, java.lang.Boolean]): Future[JOption[T]] = find(futures, predicate, timeout) +*/ + /** + * Java API. + * Returns a Future to the result of the first future in the list that is completed + */ +/* + def firstCompletedOf[T <: AnyRef](futures: JIterable[Future[T]], timeout: Timeout): Future[T] = + Future.firstCompletedOf(scala.collection.JavaConversions.iterableAsScalaIterable(futures), timeout) + + def firstCompletedOf[T <: AnyRef](futures: JIterable[Future[T]]): Future[T] = firstCompletedOf(futures, timeout) +*/ + /** + * Java API + * A non-blocking fold over the specified futures. + * The fold is performed on the thread where the last future is completed, + * the result will be the first failure of any of the futures, or any failure in the actual fold, + * or the result of the fold. + */ +/* + def fold[T <: AnyRef, R <: AnyRef](zero: R, timeout: Timeout, futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, R]): Future[R] = + Future.fold(scala.collection.JavaConversions.iterableAsScalaIterable(futures), timeout)(zero)(fun.apply _) + + def fold[T <: AnyRef, R <: AnyRef](zero: R, timeout: Long, futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, R]): Future[R] = fold(zero, timeout: Timeout, futures, fun) + + def fold[T <: AnyRef, R <: AnyRef](zero: R, futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, R]): Future[R] = fold(zero, timeout, futures, fun) + + /** + * Java API. + * Initiates a fold over the supplied futures where the fold-zero is the result value of the Future that's completed first + */ + def reduce[T <: AnyRef, R >: T](futures: java.lang.Iterable[Future[T]], timeout: Timeout, fun: akka.japi.Function2[R, T, T]): Future[R] = + Future.reduce(scala.collection.JavaConversions.iterableAsScalaIterable(futures), timeout)(fun.apply _) + + def reduce[T <: AnyRef, R >: T](futures: java.lang.Iterable[Future[T]], timeout: Long, fun: akka.japi.Function2[R, T, T]): Future[R] = reduce(futures, timeout: Timeout, fun) + + def reduce[T <: AnyRef, R >: T](futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, T]): Future[R] = reduce(futures, timeout, fun) + + /** + * Java API. + * Simple version of Future.traverse. Transforms a java.lang.Iterable[Future[A]] into a Future[java.lang.Iterable[A]]. + * Useful for reducing many Futures into a single Future. + */ + def sequence[A](in: JIterable[Future[A]], timeout: Timeout): Future[JIterable[A]] = { + implicit val t = timeout + scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[A]()))((fr, fa) ⇒ + for (r ← fr; a ← fa) yield { + r add a + r + }) + } + + def sequence[A](in: JIterable[Future[A]]): Future[JIterable[A]] = sequence(in, timeout) + + /** + * Java API. + * Transforms a java.lang.Iterable[A] into a Future[java.lang.Iterable[B]] using the provided Function A ⇒ Future[B]. + * This is useful for performing a parallel map. For example, to apply a function to all items of a list + * in parallel. + */ + def traverse[A, B](in: JIterable[A], timeout: Timeout, fn: JFunc[A, Future[B]]): Future[JIterable[B]] = { + implicit val t = timeout + scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[B]())) { (fr, a) ⇒ + val fb = fn(a) + for (r ← fr; b ← fb) yield { + r add b + r + } + } + } + + def traverse[A, B](in: JIterable[A], fn: JFunc[A, Future[B]]): Future[JIterable[B]] = traverse(in, timeout, fn) + +*/ +} +*/ + +object Future { + /* + /** + * This method constructs and returns a Future that will eventually hold the result of the execution of the supplied body + * The execution is performed by the specified Dispatcher. + */ + def apply[T](body: ⇒ T)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[T] = { + val promise: Promise[T] = DefaultPromise[T](timeout) + + dispatcher dispatchTask { () ⇒ + promise complete { + try { + Right(body) + } catch { + case e ⇒ Left(e) + } + } + } + + promise + } + + def apply[T](body: ⇒ T, timeout: Timeout)(implicit dispatcher: MessageDispatcher): Future[T] = + apply(body)(dispatcher, timeout) + + def apply[T](body: ⇒ T, timeout: Duration)(implicit dispatcher: MessageDispatcher): Future[T] = + apply(body)(dispatcher, timeout) + + def apply[T](body: ⇒ T, timeout: Long)(implicit dispatcher: MessageDispatcher): Future[T] = + apply(body)(dispatcher, timeout) + + import scala.collection.mutable.Builder + import scala.collection.generic.CanBuildFrom + + /** + * Simple version of Futures.traverse. Transforms a Traversable[Future[A]] into a Future[Traversable[A]]. + * Useful for reducing many Futures into a single Future. + */ + def sequence[A, M[_] <: Traversable[_]](in: M[Future[A]])(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], timeout: Timeout, dispatcher: MessageDispatcher): Future[M[A]] = + in.foldLeft(new KeptPromise(Right(cbf(in))): Future[Builder[A, M[A]]])((fr, fa) ⇒ for (r ← fr; a ← fa.asInstanceOf[Future[A]]) yield (r += a)).map(_.result) + + def sequence[A, M[_] <: Traversable[_]](in: M[Future[A]], timeout: Timeout)(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], dispatcher: MessageDispatcher): Future[M[A]] = + sequence(in)(cbf, timeout, dispatcher) + + /** + * Returns a Future to the result of the first future in the list that is completed + */ + def firstCompletedOf[T](futures: Iterable[Future[T]])(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[T] = { + val futureResult = DefaultPromise[T](timeout) + + val completeFirst: Future[T] ⇒ Unit = _.value.foreach(futureResult complete _) + futures.foreach(_ onComplete completeFirst) + + futureResult + } + + def firstCompletedOf[T](futures: Iterable[Future[T]], timeout: Timeout)(implicit dispatcher: MessageDispatcher): Future[T] = + firstCompletedOf(futures)(dispatcher, timeout) + + /** + * Returns a Future that will hold the optional result of the first Future with a result that matches the predicate + */ + def find[T](futures: Iterable[Future[T]])(predicate: T ⇒ Boolean)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[Option[T]] = { + if (futures.isEmpty) new KeptPromise[Option[T]](Right(None)) + else { + val result = DefaultPromise[Option[T]](timeout) + val ref = new AtomicInteger(futures.size) + val search: Future[T] ⇒ Unit = f ⇒ try { + f.result.filter(predicate).foreach(r ⇒ result completeWithResult Some(r)) + } finally { + if (ref.decrementAndGet == 0) + result completeWithResult None + } + futures.foreach(_ onComplete search) + + result + } + } + + def find[T](futures: Iterable[Future[T]], timeout: Timeout)(predicate: T ⇒ Boolean)(implicit dispatcher: MessageDispatcher): Future[Option[T]] = + find(futures)(predicate)(dispatcher, timeout) + + /** + * A non-blocking fold over the specified futures. + * The fold is performed on the thread where the last future is completed, + * the result will be the first failure of any of the futures, or any failure in the actual fold, + * or the result of the fold. + * Example: + *
+   *   val result = Futures.fold(0)(futures)(_ + _).await.result
+   * 
+ */ +/* + def fold[T, R](futures: Iterable[Future[T]])(zero: R)(foldFun: (R, T) ⇒ R)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[R] = { + if (futures.isEmpty) { + new KeptPromise[R](Right(zero)) + } else { + val result = DefaultPromise[R](timeout) + val results = new ConcurrentLinkedQueue[T]() + val done = new Switch(false) + val allDone = futures.size + + val aggregate: Future[T] ⇒ Unit = f ⇒ if (done.isOff && !result.isCompleted) { //TODO: This is an optimization, is it premature? + f.value.get match { + case Right(value) ⇒ + val added = results add value + if (added && results.size == allDone) { //Only one thread can get here + if (done.switchOn) { + try { + val i = results.iterator + var currentValue = zero + while (i.hasNext) { currentValue = foldFun(currentValue, i.next) } + result completeWithResult currentValue + } catch { + case e: Exception ⇒ + //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.fold", e.getMessage)) + result completeWithException e + } finally { + results.clear + } + } + } + case Left(exception) ⇒ + if (done.switchOn) { + result completeWithException exception + results.clear + } + } + } + + futures foreach { _ onComplete aggregate } + result + } + } +*/ +/* + def fold[T, R](futures: Iterable[Future[T]], timeout: Timeout)(zero: R)(foldFun: (R, T) ⇒ R)(implicit dispatcher: MessageDispatcher): Future[R] = + fold(futures)(zero)(foldFun)(dispatcher, timeout) +*/ + /** + * Initiates a fold over the supplied futures where the fold-zero is the result value of the Future that's completed first + * Example: + *
+   *   val result = Futures.reduce(futures)(_ + _).await.result
+   * 
+ */ +/* + def reduce[T, R >: T](futures: Iterable[Future[T]])(op: (R, T) ⇒ T)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[R] = { + if (futures.isEmpty) + new KeptPromise[R](Left(new UnsupportedOperationException("empty reduce left"))) + else { + val result = DefaultPromise[R](timeout) + val seedFound = new AtomicBoolean(false) + val seedFold: Future[T] ⇒ Unit = f ⇒ { + if (seedFound.compareAndSet(false, true)) { //Only the first completed should trigger the fold + f.value.get match { + case Right(value) ⇒ result.completeWith(fold(futures.filterNot(_ eq f))(value)(op)) + case Left(exception) ⇒ result.completeWithException(exception) + } + } + } + for (f ← futures) f onComplete seedFold //Attach the listener to the Futures + result + } + } +*/ +/* + def reduce[T, R >: T](futures: Iterable[Future[T]], timeout: Timeout)(op: (R, T) ⇒ T)(implicit dispatcher: MessageDispatcher): Future[R] = + reduce(futures)(op)(dispatcher, timeout) +*/ + /** + * Transforms a Traversable[A] into a Future[Traversable[B]] using the provided Function A ⇒ Future[B]. + * This is useful for performing a parallel map. For example, to apply a function to all items of a list + * in parallel: + *
+   * val myFutureList = Futures.traverse(myList)(x ⇒ Future(myFunc(x)))
+   * 
+ */ + def traverse[A, B, M[_] <: Traversable[_]](in: M[A])(fn: A ⇒ Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], timeout: Timeout, dispatcher: MessageDispatcher): Future[M[B]] = + in.foldLeft(new KeptPromise(Right(cbf(in))): Future[Builder[B, M[B]]]) { (fr, a) ⇒ + val fb = fn(a.asInstanceOf[A]) + for (r ← fr; b ← fb) yield (r += b) + }.map(_.result) + + def traverse[A, B, M[_] <: Traversable[_]](in: M[A], timeout: Timeout)(fn: A ⇒ Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], dispatcher: MessageDispatcher): Future[M[B]] = + traverse(in)(fn)(cbf, timeout, dispatcher) + + /** + * Captures a block that will be transformed into 'Continuation Passing Style' using Scala's Delimited + * Continuations plugin. + * + * Within the block, the result of a Future may be accessed by calling Future.apply. At that point + * execution is suspended with the rest of the block being stored in a continuation until the result + * of the Future is available. If an Exception is thrown while processing, it will be contained + * within the resulting Future. + * + * This allows working with Futures in an imperative style without blocking for each result. + * + * Completing a Future using 'Promise << Future' will also suspend execution until the + * value of the other Future is available. + * + * The Delimited Continuations compiler plugin must be enabled in order to use this method. + */ +/* + def flow[A](body: ⇒ A @cps[Future[Any]])(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[A] = { + val future = Promise[A](timeout) + dispatchTask({ () ⇒ + (reify(body) foreachFull (future completeWithResult, future completeWithException): Future[Any]) onException { + case e: Exception ⇒ future completeWithException e + } + }, true) + future + } +*/ + // TODO make variant of flow(timeout)(body) which does NOT break type inference + + /** + * Assures that any Future tasks initiated in the current thread will be + * executed asynchronously, including any tasks currently queued to be + * executed in the current thread. This is needed if the current task may + * block, causing delays in executing the remaining tasks which in some + * cases may cause a deadlock. + * + * Note: Calling 'Future.await' will automatically trigger this method. + * + * For example, in the following block of code the call to 'latch.open' + * might not be executed until after the call to 'latch.await', causing + * a deadlock. By adding 'Future.blocking()' the call to 'latch.open' + * will instead be dispatched separately from the current block, allowing + * it to be run in parallel: + *
+   * val latch = new StandardLatch
+   * val future = Future() map { _ ⇒
+   *   Future.blocking()
+   *   val nested = Future()
+   *   nested foreach (_ ⇒ latch.open)
+   *   latch.await
+   * }
+   * 
+ */ + def blocking()(implicit dispatcher: MessageDispatcher): Unit = + _taskStack.get match { + case Some(taskStack) if taskStack.nonEmpty ⇒ + val tasks = taskStack.elems + taskStack.clear() + _taskStack set None + dispatchTask(() ⇒ _taskStack.get.get.elems = tasks, true) + case Some(_) ⇒ _taskStack set None + case _ ⇒ // already None + } + + private val _taskStack = new ThreadLocal[Option[Stack[() ⇒ Unit]]]() { + override def initialValue = None + } + + private[concurrent] def dispatchTask(task: () ⇒ Unit, force: Boolean = false)(implicit dispatcher: MessageDispatcher): Unit = + _taskStack.get match { + case Some(taskStack) if !force ⇒ taskStack push task + case _ ⇒ + dispatcher dispatchTask { () ⇒ + try { + val taskStack = Stack[() ⇒ Unit](task) + _taskStack set Some(taskStack) + while (taskStack.nonEmpty) { + val next = taskStack.pop() + try { + next.apply() + } catch { + case e ⇒ e.printStackTrace() //TODO FIXME strategy for handling exceptions in callbacks + } + } + } finally { _taskStack set None } + } + } + */ +} + +//trait Future[+T] { + /* + /** + * For use only within a Future.flow block or another compatible Delimited Continuations reset block. + * + * Returns the result of this Future without blocking, by suspending execution and storing it as a + * continuation until the result is available. + */ + def apply()(implicit timeout: Timeout): T @cps[Future[Any]] = shift(this flatMap (_: T ⇒ Future[Any])) + + /** + * Blocks awaiting completion of this Future, then returns the resulting value, + * or throws the completed exception + * + * Scala & Java API + * + * throws FutureTimeoutException if this Future times out when waiting for completion + */ + def get: T = this.await.resultOrException.get + + /** + * Blocks the current thread until the Future has been completed or the + * timeout has expired. In the case of the timeout expiring a + * FutureTimeoutException will be thrown. + */ + def await: Future[T] + */ + + /** + * Blocks the current thread until the Future has been completed or the + * timeout has expired, additionally bounding the waiting period according to + * the atMost parameter. The timeout will be the lesser value of + * 'atMost' and the timeout supplied at the constructuion of this Future. In + * the case of the timeout expiring a FutureTimeoutException will be thrown. + * Other callers of this method are not affected by the additional bound + * imposed by atMost. + */ +// def await(atMost: Duration): Future[T] + +/* + /** + * Await completion of this Future and return its value if it conforms to A's + * erased type. Will throw a ClassCastException if the value does not + * conform, or any exception the Future was completed with. Will return None + * in case of a timeout. + */ + def as[A](implicit m: Manifest[A]): Option[A] = { + try await catch { case _: FutureTimeoutException ⇒ } + value match { + case None ⇒ None + case Some(Left(ex)) ⇒ throw ex + case Some(Right(v)) ⇒ + try { Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) } catch { + case c: ClassCastException ⇒ + if (v.asInstanceOf[AnyRef] eq null) throw new ClassCastException("null cannot be cast to " + m.erasure) + else throw new ClassCastException("'" + v + "' of class " + v.asInstanceOf[AnyRef].getClass + " cannot be cast to " + m.erasure) + } + } + } + + /** + * Await completion of this Future and return its value if it conforms to A's + * erased type, None otherwise. Will throw any exception the Future was + * completed with. Will return None in case of a timeout. + */ + def asSilently[A](implicit m: Manifest[A]): Option[A] = { + try await catch { case _: FutureTimeoutException ⇒ } + value match { + case None ⇒ None + case Some(Left(ex)) ⇒ throw ex + case Some(Right(v)) ⇒ + try Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) + catch { case _: ClassCastException ⇒ None } + } + } + + /** + * Tests whether this Future has been completed. + */ + final def isCompleted: Boolean = value.isDefined + + /** + * Tests whether this Future's timeout has expired. + * + * Note that an expired Future may still contain a value, or it may be + * completed with a value. + */ + def isExpired: Boolean + + def timeout: Timeout + + /** + * This Future's timeout in nanoseconds. + */ + def timeoutInNanos = if (timeout.duration.isFinite) timeout.duration.toNanos else Long.MaxValue + + /** + * The contained value of this Future. Before this Future is completed + * the value will be None. After completion the value will be Some(Right(t)) + * if it contains a valid result, or Some(Left(error)) if it contains + * an exception. + */ + def value: Option[Either[Throwable, T]] + + /** + * Returns the successful result of this Future if it exists. + */ + final def result: Option[T] = value match { + case Some(Right(r)) ⇒ Some(r) + case _ ⇒ None + } + + /** + * Returns the contained exception of this Future if it exists. + */ + final def exception: Option[Throwable] = value match { + case Some(Left(e)) ⇒ Some(e) + case _ ⇒ None + } + + /** + * When this Future is completed, apply the provided function to the + * Future. If the Future has already been completed, this will apply + * immediately. Will not be called in case of a timeout, which also holds if + * corresponding Promise is attempted to complete after expiry. Multiple + * callbacks may be registered; there is no guarantee that they will be + * executed in a particular order. + */ + def onComplete(func: Future[T] ⇒ Unit): this.type + + /** + * When the future is completed with a valid result, apply the provided + * PartialFunction to the result. See `onComplete` for more details. + *
+   *   future onResult {
+   *     case Foo ⇒ target ! "foo"
+   *     case Bar ⇒ target ! "bar"
+   *   }
+   * 
+ */ + final def onResult(pf: PartialFunction[T, Unit]): this.type = onComplete { + _.value match { + case Some(Right(r)) if pf isDefinedAt r ⇒ pf(r) + case _ ⇒ + } + } + + /** + * When the future is completed with an exception, apply the provided + * PartialFunction to the exception. See `onComplete` for more details. + *
+   *   future onException {
+   *     case NumberFormatException ⇒ target ! "wrong format"
+   *   }
+   * 
+ */ + final def onException(pf: PartialFunction[Throwable, Unit]): this.type = onComplete { + _.value match { + case Some(Left(ex)) if pf isDefinedAt ex ⇒ pf(ex) + case _ ⇒ + } + } + + def onTimeout(func: Future[T] ⇒ Unit): this.type + + def orElse[A >: T](fallback: ⇒ A): Future[A] + + /** + * Creates a new Future that will handle any matching Throwable that this + * Future might contain. If there is no match, or if this Future contains + * a valid result then the new Future will contain the same. + * Example: + *
+   * Future(6 / 0) recover { case e: ArithmeticException ⇒ 0 } // result: 0
+   * Future(6 / 0) recover { case e: NotFoundException   ⇒ 0 } // result: exception
+   * Future(6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3
+   * 
+ */ + final def recover[A >: T](pf: PartialFunction[Throwable, A])(implicit timeout: Timeout): Future[A] = { + val future = DefaultPromise[A](timeout) + onComplete { + _.value.get match { + case Left(e) if pf isDefinedAt e ⇒ future.complete(try { Right(pf(e)) } catch { case x: Exception ⇒ Left(x) }) + case otherwise ⇒ future complete otherwise + } + } + future + } + + /** + * Creates a new Future by applying a function to the successful result of + * this Future. If this Future is completed with an exception then the new + * Future will also contain this exception. + * Example: + *
+   * val future1 = for {
+   *   a: Int    <- actor ? "Hello" // returns 5
+   *   b: String <- actor ? a       // returns "10"
+   *   c: String <- actor ? 7       // returns "14"
+   * } yield b + "-" + c
+   * 
+ */ + final def map[A](f: T ⇒ A)(implicit timeout: Timeout): Future[A] = { + val future = DefaultPromise[A](timeout) + onComplete { + _.value.get match { + case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, A]] + case Right(res) ⇒ + future complete (try { + Right(f(res)) + } catch { + case e: Exception ⇒ + //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.map", e.getMessage)) + Left(e) + }) + } + } + future + } + + /** + * Creates a new Future[A] which is completed with this Future's result if + * that conforms to A's erased type or a ClassCastException otherwise. + */ + final def mapTo[A](implicit m: Manifest[A], timeout: Timeout = this.timeout): Future[A] = { + val fa = DefaultPromise[A](timeout) + onComplete { ft ⇒ + fa complete (ft.value.get match { + case l: Left[_, _] ⇒ l.asInstanceOf[Either[Throwable, A]] + case Right(t) ⇒ + try { + Right(BoxedType(m.erasure).cast(t).asInstanceOf[A]) + } catch { + case e: ClassCastException ⇒ Left(e) + } + }) + } + fa + } + + /** + * Creates a new Future by applying a function to the successful result of + * this Future, and returns the result of the function as the new Future. + * If this Future is completed with an exception then the new Future will + * also contain this exception. + * Example: + *
+   * val future1 = for {
+   *   a: Int    <- actor ? "Hello" // returns 5
+   *   b: String <- actor ? a       // returns "10"
+   *   c: String <- actor ? 7       // returns "14"
+   * } yield b + "-" + c
+   * 
+ */ + final def flatMap[A](f: T ⇒ Future[A])(implicit timeout: Timeout): Future[A] = { + val future = DefaultPromise[A](timeout) + + onComplete { + _.value.get match { + case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, A]] + case Right(r) ⇒ try { + future.completeWith(f(r)) + } catch { + case e: Exception ⇒ + //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.flatMap", e.getMessage)) + future complete Left(e) + } + } + } + future + } + + final def foreach(f: T ⇒ Unit): Unit = onComplete { + _.value.get match { + case Right(r) ⇒ f(r) + case _ ⇒ + } + } + + final def withFilter(p: T ⇒ Boolean)(implicit timeout: Timeout) = new FutureWithFilter[T](this, p) + + final class FutureWithFilter[+A](self: Future[A], p: A ⇒ Boolean)(implicit timeout: Timeout) { + def foreach(f: A ⇒ Unit): Unit = self filter p foreach f + def map[B](f: A ⇒ B): Future[B] = self filter p map f + def flatMap[B](f: A ⇒ Future[B]): Future[B] = self filter p flatMap f + def withFilter(q: A ⇒ Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) + } + + final def filter(p: T ⇒ Boolean)(implicit timeout: Timeout): Future[T] = { + val future = DefaultPromise[T](timeout) + onComplete { + _.value.get match { + case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, T]] + case r @ Right(res) ⇒ future complete (try { + if (p(res)) r else Left(new MatchError(res)) + } catch { + case e: Exception ⇒ + //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.filter", e.getMessage)) + Left(e) + }) + } + } + future + } + + /** + * Returns the current result, throws the exception if one has been raised, else returns None + */ + final def resultOrException: Option[T] = value match { + case Some(Left(e)) ⇒ throw e + case Some(Right(r)) ⇒ Some(r) + case _ ⇒ None + } + */ +//} + +/** + * Essentially this is the Promise (or write-side) of a Future (read-side). + */ +//trait Promise[T] extends Future[T] { + + /* + def start(): Unit + + /** + * Completes this Future with the specified result, if not already completed. + * @return this + */ + def complete(value: Either[Throwable, T]): this.type + + /** + * Completes this Future with the specified result, if not already completed. + * @return this + */ + final def completeWithResult(result: T): this.type = complete(Right(result)) + + /** + * Completes this Future with the specified exception, if not already completed. + * @return this + */ + final def completeWithException(exception: Throwable): this.type = complete(Left(exception)) + + /** + * Completes this Future with the specified other Future, when that Future is completed, + * unless this Future has already been completed. + * @return this. + */ + final def completeWith(other: Future[T]): this.type = { + other onComplete { f ⇒ complete(f.value.get) } + this + } + */ +/* + final def <<(value: T): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ cont(complete(Right(value))) } + + final def <<(other: Future[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ + val fr = DefaultPromise[Any](this.timeout) + this completeWith other onComplete { f ⇒ + try { + fr completeWith cont(f) + } catch { + case e: Exception ⇒ + //dispatcher.prerequisites.eventStream.publish(Error(e, "Promise.completeWith", e.getMessage)) + fr completeWithException e + } + } + fr + } + + final def <<(stream: PromiseStreamOut[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ + val fr = DefaultPromise[Any](this.timeout) + stream.dequeue(this).onComplete { f ⇒ + try { + fr completeWith cont(f) + } catch { + case e: Exception ⇒ + //dispatcher.prerequisites.eventStream.publish(Error(e, "Promise.completeWith", e.getMessage)) + fr completeWithException e + } + } + fr + } +*/ +//} + +/** + * Represents the internal state of the DefaultCompletableFuture + */ +sealed abstract class FState[+T] { def value: Option[Either[Throwable, T]] } + +case class Pending[T](listeners: List[Future[T] ⇒ Unit] = Nil) extends FState[T] { + def value: Option[Either[Throwable, T]] = None +} + +case class Success[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def result: T = value.get.right.get +} + +case class Failure[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def exception: Throwable = value.get.left.get +} + +case object Expired extends FState[Nothing] { + def value: Option[Either[Throwable, Nothing]] = None +} + +//Companion object to FState, just to provide a cheap, immutable default entry +private[concurrent] object FState { + def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] + private val emptyPendingValue = Pending[Nothing](Nil) +} + +private[concurrent] object DefaultPromise { + def apply[T](within: Timeout)(implicit disp: MessageDispatcher): Promise[T] = new DefaultPromise[T] { + val timeout = within + implicit val dispatcher = disp + } + + def apply[T]()(implicit dispatcher: MessageDispatcher, timeout: Timeout): Promise[T] = apply(timeout) + + def apply[T](timeout: Long)(implicit dispatcher: MessageDispatcher): Promise[T] = apply(Timeout(timeout)) + + def apply[T](timeout: Long, timeunit: TimeUnit)(implicit dispatcher: MessageDispatcher): Promise[T] = apply(Timeout(timeout, timeunit)) +} + +/** + * The default concrete Future implementation. + */ +trait DefaultPromise[T] extends /*AbstractPromise with*/ Promise[T] { + self ⇒ + +// @volatile private _ref: AnyRef = DefaultPromise.EmptyPending() + +// protected final static AtomicReferenceFieldUpdater updater = +// AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); + + val timeout: Timeout + implicit val dispatcher: MessageDispatcher + + private val _startTimeInNanos = currentTimeInNanos + + @tailrec + private def awaitUnsafe(waitTimeNanos: Long): Boolean = { + if (value.isEmpty && waitTimeNanos > 0) { + val ms = NANOS.toMillis(waitTimeNanos) + val ns = (waitTimeNanos % 1000000l).toInt //As per object.wait spec + val start = currentTimeInNanos + try { synchronized { if (value.isEmpty) wait(ms, ns) } } catch { case e: InterruptedException ⇒ } + + awaitUnsafe(waitTimeNanos - (currentTimeInNanos - start)) + } else { + value.isDefined + } + } + + def await(atMost: Duration): this.type = if (value.isDefined) this else { + Future.blocking() + + val waitNanos = + if (timeout.duration.isFinite && atMost.isFinite) + atMost.toNanos min timeLeft() + else if (atMost.isFinite) + atMost.toNanos + else if (timeout.duration.isFinite) + timeLeft() + else Long.MaxValue //If both are infinite, use Long.MaxValue + + if (awaitUnsafe(waitNanos)) this + else throw new FutureTimeoutException("Futures timed out after [" + NANOS.toMillis(waitNanos) + "] milliseconds") + } + + def await = await(timeout.duration) + + def isExpired: Boolean = if (timeout.duration.isFinite) timeLeft() <= 0 else false + + def value: Option[Either[Throwable, T]] = getState.value + + @inline + protected final def updateState(oldState: FState[T], newState: FState[T]): Boolean = + AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]].compareAndSet(this, oldState, newState) + + @inline + protected final def getState: FState[T] = { + + @tailrec + def read(): FState[T] = { + val cur = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]].get(this) + if (cur.isInstanceOf[Pending[_]] && isExpired) { + if (updateState(cur, Expired)) Expired else read() + } else cur + } + + read() + } + + def complete(value: Either[Throwable, T]): this.type = { + val callbacks = { + try { + @tailrec + def tryComplete: List[Future[T] ⇒ Unit] = { + val cur = getState + + cur match { + case Pending(listeners) ⇒ + if (updateState(cur, if (value.isLeft) Failure(Some(value)) else Success(Some(value)))) listeners + else tryComplete + case _ ⇒ Nil + } + } + tryComplete + } finally { + synchronized { notifyAll() } //Notify any evil blockers + } + } + + if (callbacks.nonEmpty) Future.dispatchTask(() ⇒ callbacks foreach notifyCompleted) + + this + } + + def onComplete(func: Future[T] ⇒ Unit): this.type = { + @tailrec //Returns whether the future has already been completed or not + def tryAddCallback(): Boolean = { + val cur = getState + cur match { + case _: Success[_] | _: Failure[_] ⇒ true + case Expired ⇒ false + case p: Pending[_] ⇒ + val pt = p.asInstanceOf[Pending[T]] + if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false + else tryAddCallback() + } + } + + if (tryAddCallback()) Future.dispatchTask(() ⇒ notifyCompleted(func)) + + this + } + + def onTimeout(func: Future[T] ⇒ Unit): this.type = { + val runNow = + if (!timeout.duration.isFinite) false //Not possible + else if (value.isEmpty) { + if (!isExpired) { + val runnable = new Runnable { + def run() { + if (!isCompleted) { + if (!isExpired) { + // TODO FIXME: add scheduler + //dispatcher.prerequisites.scheduler.scheduleOnce(this, timeLeftNoinline(), NANOS) + } else func(DefaultPromise.this) + } + } + } + /* + TODO FIXME: add scheduler + val timeoutFuture = dispatcher.prerequisites.scheduler.scheduleOnce(runnable, timeLeft(), NANOS) + onComplete(_ ⇒ timeoutFuture.cancel()) + */ + false + } else true + } else false + + if (runNow) Future.dispatchTask(() ⇒ notifyCompleted(func)) + + this + } + + final def orElse[A >: T](fallback: ⇒ A): Future[A] = + if (timeout.duration.isFinite) { + getState match { + case _: Success[_] | _: Failure[_] ⇒ this + case Expired ⇒ Future[A](fallback, timeout) + case _: Pending[_] ⇒ + val promise = DefaultPromise[A](Timeout.never) //TODO FIXME We can't have infinite timeout here, doesn't make sense. + promise completeWith this + val runnable = new Runnable { + def run() { + if (!isCompleted) { + if (!isExpired) { + // TODO FIXME add scheduler + //dispatcher.prerequisites.scheduler.scheduleOnce(this, timeLeftNoinline(), NANOS) + } else promise complete (try { Right(fallback) } catch { case e ⇒ Left(e) }) + } + } + } + // TODO FIXME add + //dispatcher.prerequisites.scheduler.scheduleOnce(runnable, timeLeft(), NANOS) + promise + } + } else this + + private def notifyCompleted(func: Future[T] ⇒ Unit) { + try { func(this) } catch { case e ⇒ /*dispatcher.prerequisites.eventStream.publish(Error(e, "Future", "Future onComplete-callback raised an exception"))*/ } //TODO catch, everything? Really? + } + + @inline + private def currentTimeInNanos: Long = MILLIS.toNanos(System.currentTimeMillis) //TODO Switch to math.abs(System.nanoTime)? + //TODO: the danger of Math.abs is that it could break the ordering of time. So I would not recommend an abs. + @inline + private def timeLeft(): Long = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos) + + private def timeLeftNoinline(): Long = timeLeft() +//} + +/** + * An already completed Future is seeded with it's result at creation, is useful for when you are participating in + * a Future-composition but you already have a value to contribute. + */ +sealed class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val dispatcher: MessageDispatcher) extends Promise[T] { + val value = Some(suppliedValue) + + def complete(value: Either[Throwable, T]): this.type = this + def onComplete(func: Future[T] ⇒ Unit): this.type = { + Future dispatchTask (() ⇒ func(this)) + this + } + def await(atMost: Duration): this.type = this + def await: this.type = this + def isExpired: Boolean = true + def timeout: Timeout = Timeout.zero + + final def onTimeout(func: Future[T] ⇒ Unit): this.type = this + final def orElse[A >: T](fallback: ⇒ A): Future[A] = this + */ +//} + +object BoxedType { + + private val toBoxed = Map[Class[_], Class[_]]( + classOf[Boolean] -> classOf[jl.Boolean], + classOf[Byte] -> classOf[jl.Byte], + classOf[Char] -> classOf[jl.Character], + classOf[Short] -> classOf[jl.Short], + classOf[Int] -> classOf[jl.Integer], + classOf[Long] -> classOf[jl.Long], + classOf[Float] -> classOf[jl.Float], + classOf[Double] -> classOf[jl.Double], + classOf[Unit] -> classOf[scala.runtime.BoxedUnit]) + + def apply(c: Class[_]): Class[_] = { + if (c.isPrimitive) toBoxed(c) else c + } + +} -- cgit v1.2.3 From f01ab43357b2b565d94ec09bdef82f2437da0db9 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Mon, 5 Dec 2011 16:43:22 +0100 Subject: The default implementations for some of the future methods. --- .../scala/concurrent/ExecutionContext.scala | 14 ++++--- src/library/scala/concurrent/Future.scala | 47 +++++++++++++++++----- src/library/scala/concurrent/Promise.scala | 30 ++++++++++++++ src/library/scala/concurrent/package.scala | 9 ++++- 4 files changed, 85 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 972a76a95a..5ab712b89a 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -1,30 +1,34 @@ package scala.concurrent + import java.util.concurrent.{ Executors, Future => JFuture } import scala.util.{ Duration, Timeout } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } -trait ExecutionContext { +trait ExecutionContext { + protected implicit object CanBlockEvidence extends CanBlock def execute(task: Runnable): Unit def makeTask[T](task: () => T)(implicit timeout: Timeout): Task[T] - def makePromise[T](timeout: Timeout): Promise[T] + def makePromise[T](implicit timeout: Timeout): Promise[T] def blockingCall[T](body: Blockable[T]): T - + } -trait Task[T] { +trait Task[T] { + def start(): Unit def future: Future[T] - + } + /* DONE: The challenge is to make ForkJoinPromise inherit from RecursiveAction * to avoid an object allocation per promise. This requires turning DefaultPromise * into a trait, i.e., removing its constructor parameters. diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 222c5d18b6..16741ad50c 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -42,7 +42,7 @@ import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, * Futures obtained through combinators have the same exception as the future they were obtained from. * The following throwable objects are treated differently: * - Error - errors are not contained within futures - * - NonLocalControlException - not contained within futures + * - scala.util.control.ControlException - not contained within futures * - InterruptedException - not contained within futures * * @define forComprehensionExamples @@ -64,6 +64,7 @@ import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, * }}} */ trait Future[+T] extends Blockable[T] { +self => /* Callbacks */ @@ -79,7 +80,9 @@ trait Future[+T] extends Blockable[T] { * * $multipleCallbacks */ - def onSuccess[U](func: T => U): this.type + def onSuccess[U](func: T => U): this.type = onComplete { + case Right(v) => func(v) + } /** When this future is completed with a failure (i.e. with a throwable), * apply the provided function to the throwable. @@ -95,7 +98,9 @@ trait Future[+T] extends Blockable[T] { * * $multipleCallbacks */ - def onFailure[U](func: Throwable => U): this.type + def onFailure[U](func: Throwable => U): this.type = onComplete { + case Left(t) if isFutureThrowable(t) => func(t) + } /** When this future times out, apply the provided function. * @@ -104,7 +109,9 @@ trait Future[+T] extends Blockable[T] { * * $multipleCallbacks */ - def onTimeout[U](func: => U): this.type + def onTimeout[U](body: =>U): this.type = onComplete { + case Left(te: TimeoutException) => body + } /** When this future is completed, either through an exception, a timeout, or a value, * apply the provided function. @@ -143,9 +150,23 @@ trait Future[+T] extends Blockable[T] { /* Projections */ - def failed: Future[Exception] - - def timedout: Future[Timeout] + def failed: Future[Throwable] = new Future[Throwable] { + def onComplete[U](func: Either[Throwable, Throwable] => U) = self.onComplete { + case Left(t) => func(Right(t)) + case Right(v) => // do nothing + } + def isTimedout = self.isTimedout + def timeout = self.timeout + } + + def timedout: Future[TimeoutException] = new Future[TimeoutException] { + def onComplete[U](func: Either[Throwable, TimeoutException] => U) = self.onComplete { + case Left(te: TimeoutException) => func(Right(te)) + case _ => // do nothing + } + def isTimedout = self.isTimedout + def timeout = self.timeout + } /* Monadic operations */ @@ -209,6 +230,14 @@ trait Future[+T] extends Blockable[T] { } -class FutureTimeoutException(message: String, cause: Throwable = null) extends Exception(message, cause) { - def this(message: String) = this(message, null) + +/** A timeout exception. + * + * Futures are failed with a timeout exception when their timeout expires. + * + * Each timeout exception contains an origin future which originally timed out. + */ +class TimeoutException(origin: Future[T], message: String, cause: Throwable = null) extends Exception(message, cause) { + def this(origin: Future[T], message: String) = this(origin, message, null) + def this(origin: Future[T]) = this(origin, "Future timed out.") } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 109f6b4eba..4c6b11dfd6 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -8,13 +8,43 @@ package scala.concurrent + + +/** Promise is an object which can be completed with a value or failed + * with an exception. + * + * A promise is assigned a timeout when created. After the timeout expires, + * the promise will be failed with a TimeoutException. + * + * @promiseCompletion + * If the promise has already been fulfilled, failed or has timed out, + * calling this method will throw an IllegalStateException. + */ trait Promise[T] { def future: Future[T] + /** Completes the promise with a value. + * + * @param value The value to complete the promise with. + * + * $promiseCompletion + */ def fulfill(value: T): Unit + + /** Completes the promise with an exception. + * + * @param t The throwable to complete the promise with. + * + * $promiseCompletion + */ def fail(t: Throwable): Unit + + /** The timeout for this promise. + */ + def timeout: Timeout } + object Promise { /* /** diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 2a1ff9b799..64ccdeb0ab 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -36,9 +36,16 @@ package object concurrent { } } - def future[T](body: =>T): Future[T] = null // TODO + // TODO rename appropriately and make public + private[concurrent] def isFutureThrowable(t: Throwable) = t match { + case e: Error => false + case t: scala.util.control.ControlThrowable => false + case i: InterruptException => false + case _ => true + } + } -- cgit v1.2.3 From 730564354514a9e6d3506f0f1211fa4032cfafc9 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Mon, 5 Dec 2011 17:33:38 +0100 Subject: Completed default implementations of monadic ops on futures. --- src/library/scala/concurrent/Future.scala | 56 +++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 16741ad50c..361bed4d2b 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -124,7 +124,11 @@ self => def onComplete[U](func: Either[Throwable, T] => U): this.type - /* Various info */ + /* Miscellaneous */ + + /** Creates a new promise. + */ + def newPromise[S]: Promise[S] /** Tests whether this Future's timeout has expired. * @@ -151,6 +155,7 @@ self => /* Projections */ def failed: Future[Throwable] = new Future[Throwable] { + def newPromise[S] = self.newPromise[S] def onComplete[U](func: Either[Throwable, Throwable] => U) = self.onComplete { case Left(t) => func(Right(t)) case Right(v) => // do nothing @@ -160,6 +165,7 @@ self => } def timedout: Future[TimeoutException] = new Future[TimeoutException] { + def newPromise[S] = self.newPromise[S] def onComplete[U](func: Either[Throwable, TimeoutException] => U) = self.onComplete { case Left(te: TimeoutException) => func(Right(te)) case _ => // do nothing @@ -183,7 +189,16 @@ self => * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3 * }}} */ - def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit timeout: Timeout): Future[U] + def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit timeout: Timeout): Future[U] = { + val p = newPromise[U] + + onComplete { + case Left(t) => if (pf isDefinedAt t) p fulfill pf(t) else p fail t + case Right(v) => p fulfill v + } + + p.future + } /** Asynchronously processes the value in the future once the value becomes available. * @@ -191,7 +206,7 @@ self => * * This method typically registers an `onResult` callback. */ - def foreach[U](f: T => U): Unit + def foreach[U](f: T => U): Unit = onResult f /** Creates a new future by applying a function to the successful result of * this future. If this future is completed with an exception then the new @@ -199,7 +214,16 @@ self => * * $forComprehensionExample */ - def map[S](f: T => S)(implicit timeout: Timeout): Future[S] + def map[S](f: T => S)(implicit timeout: Timeout): Future[S] = { + val p = newPromise[S] + + onComplete { + case Left(t) => p fail t + case Right(v) => p fulfill f(v) + } + + p.future + } /** Creates a new future by applying a function to the successful result of * this future, and returns the result of the function as the new future. @@ -208,7 +232,18 @@ self => * * $forComprehensionExample */ - def flatMap[S](f: T => Future[S])(implicit timeout: Timeout): Future[S] + def flatMap[S](f: T => Future[S])(implicit timeout: Timeout): Future[S] = { + val p = newPromise[S] + + onComplete { + case Left(t) => p fail t + case Right(f) => f onComplete { + p fulfill _ + } + } + + p.future + } /** Creates a new future by filtering the value of the current future with a predicate. * @@ -226,7 +261,16 @@ self => * block on h // throw a NoSuchElementException * }}} */ - def filter(p: T => Boolean)(implicit timeout: Timeout): Future[T] + def filter(p: T => Boolean)(implicit timeout: Timeout): Future[T] = { + val p = newPromise[T] + + onComplete { + case Left(t) => p fail t + case Right(v) => if (p(v)) p fulfill v else p fail new NoSuchElementException + } + + p.future + } } -- cgit v1.2.3 From 65e3857abce4c84f95aec5f689104d584b6968aa Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Tue, 6 Dec 2011 16:50:36 +0100 Subject: Adding some comments to block on. Removing timeout parameters. --- src/library/scala/concurrent/ExecutionContext.scala | 1 + src/library/scala/concurrent/Future.scala | 13 +++++-------- src/library/scala/concurrent/Promise.scala | 5 ++++- src/library/scala/concurrent/package.scala | 21 +++++++++++++++++++++ 4 files changed, 31 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 5ab712b89a..4a3e112c1a 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -24,6 +24,7 @@ trait ExecutionContext { trait Task[T] { def start(): Unit + def future: Future[T] } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 361bed4d2b..2393efcef6 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -30,8 +30,6 @@ import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, * The timeout of the future is: * - if this future was obtained from a task (i.e. by calling `task.future`), the timeout associated with that task * - if this future was obtained from a promise (i.e. by calling `promise.future`), the timeout associated with that promise - * - if this future was obtained from a combinator on some other future `g` (e.g. by calling `g.map(_)`), the timeout of `g` - * - if this future was obtained from a combinator on multiple futures `g0`, ..., `g1`, the minimum of the timeouts of these futures * * @define multipleCallbacks * Multiple callbacks may be registered; there is no guarantee that they will be @@ -189,7 +187,7 @@ self => * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3 * }}} */ - def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit timeout: Timeout): Future[U] = { + def recover[U >: T](pf: PartialFunction[Throwable, U]): Future[U] = { val p = newPromise[U] onComplete { @@ -214,7 +212,7 @@ self => * * $forComprehensionExample */ - def map[S](f: T => S)(implicit timeout: Timeout): Future[S] = { + def map[S](f: T => S): Future[S] = { val p = newPromise[S] onComplete { @@ -232,7 +230,7 @@ self => * * $forComprehensionExample */ - def flatMap[S](f: T => Future[S])(implicit timeout: Timeout): Future[S] = { + def flatMap[S](f: T => Future[S]): Future[S] = { val p = newPromise[S] onComplete { @@ -261,7 +259,7 @@ self => * block on h // throw a NoSuchElementException * }}} */ - def filter(p: T => Boolean)(implicit timeout: Timeout): Future[T] = { + def filter(p: T => Boolean): Future[T] = { val p = newPromise[T] onComplete { @@ -281,7 +279,6 @@ self => * * Each timeout exception contains an origin future which originally timed out. */ -class TimeoutException(origin: Future[T], message: String, cause: Throwable = null) extends Exception(message, cause) { - def this(origin: Future[T], message: String) = this(origin, message, null) +class TimeoutException(origin: Future[T], message: String) extends java.util.concurrent.TimeoutException(message) { def this(origin: Future[T]) = this(origin, "Future timed out.") } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 4c6b11dfd6..c5336ab00f 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -16,11 +16,14 @@ package scala.concurrent * A promise is assigned a timeout when created. After the timeout expires, * the promise will be failed with a TimeoutException. * - * @promiseCompletion + * @define promiseCompletion * If the promise has already been fulfilled, failed or has timed out, * calling this method will throw an IllegalStateException. */ trait Promise[T] { + + /** Future containing the value of this promise. + */ def future: Future[T] /** Completes the promise with a value. diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 64ccdeb0ab..74e8b71eff 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -23,11 +23,32 @@ package object concurrent { override protected def initialValue = null } + /** The keyword used to block on a piece of code which potentially blocks. + * + * @define mayThrow + * Calling this method may throw the following exceptions: + * - CancellationException - if the computation was cancelled + * - InterruptedException - in the case that a wait within the blockable object was interrupted + * - TimeoutException - in the case that the blockable object timed out + */ object block { + + /** Blocks on a piece of code. + * + * @param body A piece of code which contains potentially blocking or long running calls. + * + * $mayThrow + */ def on[T](body: =>T): T = on(new Blockable[T] { def block()(implicit cb: CanBlock) = body }) + /** Blocks on a blockable object. + * + * @param blockable An object with a `block` method which runs potentially blocking or long running calls. + * + * $mayThrow + */ def on[T](blockable: Blockable[T]): T = { currentExecutionContext.get match { case null => blockable.block()(null) // outside -- cgit v1.2.3 From 539aa7b6006b886fca3f963aa9d646e3511670b7 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 10:21:04 +0100 Subject: A slight refactoring in promise and task factory methods. --- src/library/scala/concurrent/ExecutionContext.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 4a3e112c1a..230f0de388 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -12,9 +12,9 @@ trait ExecutionContext { def execute(task: Runnable): Unit - def makeTask[T](task: () => T)(implicit timeout: Timeout): Task[T] + def task[T](task: () => T)(implicit timeout: Timeout): Task[T] - def makePromise[T](implicit timeout: Timeout): Promise[T] + def promise[T](implicit timeout: Timeout): Promise[T] def blockingCall[T](body: Blockable[T]): T -- cgit v1.2.3 From a36175feb5bfce59909fa4f3d9d5df6753b6ee3a Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 11:59:35 +0100 Subject: Implementation of the fj task and future. --- .../scala/concurrent/ExecutionContext.scala | 92 -------------- .../scala/concurrent/ForkJoinTaskImpl.scala | 133 +++++++++++++++++++++ src/library/scala/concurrent/Future.scala | 23 ++-- src/library/scala/concurrent/Task.scala | 13 ++ src/library/scala/concurrent/package.scala | 20 +++- src/library/scala/package.scala | 3 +- 6 files changed, 175 insertions(+), 109 deletions(-) create mode 100644 src/library/scala/concurrent/ForkJoinTaskImpl.scala create mode 100644 src/library/scala/concurrent/Task.scala (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 230f0de388..34c14147f5 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -21,98 +21,6 @@ trait ExecutionContext { } -trait Task[T] { - - def start(): Unit - - def future: Future[T] - -} - - -/* DONE: The challenge is to make ForkJoinPromise inherit from RecursiveAction - * to avoid an object allocation per promise. This requires turning DefaultPromise - * into a trait, i.e., removing its constructor parameters. - */ -private[concurrent] class ForkJoinTaskImpl[T](context: ForkJoinExecutionContext, body: () => T, within: Timeout) extends FJTask[T] with Task[T] { - - val timeout = within - implicit val dispatcher = context - - // body of RecursiveTask - def compute(): T = - body() - - def start(): Unit = - fork() - - def future: Future[T] = { - null - } - - // TODO FIXME: handle timeouts - def await(atMost: Duration): this.type = - await - - def await: this.type = { - this.join() - this - } - - def tryCancel(): Unit = - tryUnfork() -} - -private[concurrent] final class ForkJoinExecutionContext extends ExecutionContext { - val pool = new ForkJoinPool - - @inline - private def executeForkJoinTask(task: RecursiveAction) { - if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) - task.fork() - else - pool execute task - } - - def execute(task: Runnable) { - val action = new RecursiveAction { def compute() { task.run() } } - executeForkJoinTask(action) - } - - def makeTask[T](body: () => T)(implicit timeout: Timeout): Task[T] = { - new ForkJoinTaskImpl(this, body, timeout) - } - - def makePromise[T](timeout: Timeout): Promise[T] = - null - - def blockingCall[T](body: Blockable[T]): T = - body.block()(CanBlockEvidence) - -} - -/** - * Implements a blocking execution context - */ -/* -private[concurrent] class BlockingExecutionContext extends ExecutionContext { - //val pool = makeCachedThreadPool // TODO FIXME: need to merge thread pool factory methods from Heather's parcolls repo - - def execute(task: Runnable) { - /* TODO - val p = newPromise(task.run()) - p.start() - pool execute p - */ - } - - // TODO FIXME: implement - def newPromise[T](body: => T): Promise[T] = { - throw new Exception("not yet implemented") - } -} -*/ - object ExecutionContext { lazy val forNonBlocking = new ForkJoinExecutionContext diff --git a/src/library/scala/concurrent/ForkJoinTaskImpl.scala b/src/library/scala/concurrent/ForkJoinTaskImpl.scala new file mode 100644 index 0000000000..6a33ca162a --- /dev/null +++ b/src/library/scala/concurrent/ForkJoinTaskImpl.scala @@ -0,0 +1,133 @@ +package scala.concurrent + + + +import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater +import scala.annotation.tailrec + + + +/* DONE: The challenge is to make ForkJoinPromise inherit from RecursiveAction + * to avoid an object allocation per promise. This requires turning DefaultPromise + * into a trait, i.e., removing its constructor parameters. + */ +private[concurrent] class ForkJoinTaskImpl[T](val executionContext: ForkJoinExecutionContext, val body: () => T, val timeout: Timeout) +extends FJTask[T] with Task[T] with Future[T] { + + private val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[ForkJoinTaskImpl[T]], classOf[FJState[T]], "state") + @volatile private var state: State[T] = _ + + updater.set(this, Pending(List())) + + private def casState(oldv: State[T], newv: State[T]) = { + updater.compareAndSet(this, oldv, newv) + } + + @tailrec private def trySucceedState(res: T): Unit = updater.get(this) match { + case p @ Pending(cbs) => if (!casState(p, Success(res))) trySucceedState(res) + case _ => // return + } + + @tailrec private def tryFailState(t: Throwable): Unit = updater.get(this) match { + case p @ Pending(cbs) => if (!casState(p, Failure(t))) tryFailState(t) + case _ => // return + } + + // body of RecursiveTask + def compute(): T = { + try { + val res = body() + trySucceedState(res) + } catch handledFutureException andThen { + t => tryFailState(t) + } finally tryFailState(new ExecutionException) + } + + def start(): Unit = { + Thread.currentThread match { + case fj: ForkJoinWorkerThread if fj.pool eq executionContext.pool => fork() + case _ => executionContext.pool.execute(this) + } + } + + def future: Future[T] = this + + def onComplete[U](callback: Either[Throwable, T] => U): this.type = { + @tailrec def tryAddCallback(): Either[Throwable, T] = { + updater.get(this) match { + case p @ Pending(lst) => + val pt = p.asInstanceOf[Pending[T]] + if (casState(pt, Pending(callback :: pt.lst))) null + else tryAddCallback() + case Success(res) => Right(res) + case Failure(t) => Left(t) + } + } + + val res = tryAddCallback() + if (res != null) dispatchTask new Runnable { + override def run() = + try callback(res) + catch handledFutureException + } + } + + private def dispatchTask[U](r: Runnable) = executionContext execute r + + def isTimedout: Boolean = false // TODO + + // TODO FIXME: handle timeouts + def await(atMost: Duration): this.type = + await + + def await: this.type = { + this.join() + this + } + + def tryCancel(): Unit = + tryUnfork() + +} + + +private[concurrent] sealed abstract class FJState[T] + + +case class Pending[T](callbacks: List[Either[Throwable, T] => Any]) extends FJState[T] + + +case class Success[T](result: T) extends FJState[T] + + +case class Failure[T](throwable: Throwable) extends FJState[T] + + +private[concurrent] final class ForkJoinExecutionContext extends ExecutionContext { + val pool = new ForkJoinPool + + @inline + private def executeForkJoinTask(task: RecursiveAction) { + if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) + task.fork() + else + pool execute task + } + + def execute(task: Runnable) { + val action = new RecursiveAction { def compute() { task.run() } } + executeForkJoinTask(action) + } + + def makeTask[T](body: () => T)(implicit timeout: Timeout): Task[T] = { + new ForkJoinTaskImpl(this, body, timeout) + } + + def makePromise[T](timeout: Timeout): Promise[T] = + null + + def blockingCall[T](body: Blockable[T]): T = + body.block()(CanBlockEvidence) + +} diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 2393efcef6..b65d777d67 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -108,7 +108,7 @@ self => * $multipleCallbacks */ def onTimeout[U](body: =>U): this.type = onComplete { - case Left(te: TimeoutException) => body + case Left(te: FutureTimeoutException) => body } /** When this future is completed, either through an exception, a timeout, or a value, @@ -124,9 +124,13 @@ self => /* Miscellaneous */ + /** The execution context of the future. + */ + def executionContext: ExecutionContext + /** Creates a new promise. */ - def newPromise[S]: Promise[S] + def newPromise[S]: Promise[S] = executionContext promise /** Tests whether this Future's timeout has expired. * @@ -162,10 +166,10 @@ self => def timeout = self.timeout } - def timedout: Future[TimeoutException] = new Future[TimeoutException] { + def timedout: Future[FutureTimeoutException] = new Future[FutureTimeoutException] { def newPromise[S] = self.newPromise[S] - def onComplete[U](func: Either[Throwable, TimeoutException] => U) = self.onComplete { - case Left(te: TimeoutException) => func(Right(te)) + def onComplete[U](func: Either[Throwable, FutureTimeoutException] => U) = self.onComplete { + case Left(te: FutureTimeoutException) => func(Right(te)) case _ => // do nothing } def isTimedout = self.isTimedout @@ -273,12 +277,3 @@ self => } -/** A timeout exception. - * - * Futures are failed with a timeout exception when their timeout expires. - * - * Each timeout exception contains an origin future which originally timed out. - */ -class TimeoutException(origin: Future[T], message: String) extends java.util.concurrent.TimeoutException(message) { - def this(origin: Future[T]) = this(origin, "Future timed out.") -} diff --git a/src/library/scala/concurrent/Task.scala b/src/library/scala/concurrent/Task.scala new file mode 100644 index 0000000000..98c7da77d2 --- /dev/null +++ b/src/library/scala/concurrent/Task.scala @@ -0,0 +1,13 @@ +package scala.concurrent + + + +trait Task[T] { + + def start(): Unit + + def future: Future[T] + +} + + diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 74e8b71eff..51bb1ac3e0 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -16,8 +16,10 @@ package scala /** This package object contains primitives for parallel programming. */ package object concurrent { - - type MessageDispatcher = ExecutionContext // TODO FIXME: change futures to use execution context + + type ExecutionException = java.util.concurrent.ExecutionException + type CancellationException = java.util.concurrent.CancellationException + type TimeoutException = java.util.concurrent.TimeoutException private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { override protected def initialValue = null @@ -59,6 +61,10 @@ package object concurrent { def future[T](body: =>T): Future[T] = null // TODO + val handledFutureException: PartialFunction[Throwable, Throwable] = { + case t: Throwable if isFutureThrowable => t + } + // TODO rename appropriately and make public private[concurrent] def isFutureThrowable(t: Throwable) = t match { case e: Error => false @@ -74,4 +80,14 @@ package concurrent { private[concurrent] trait CanBlock + /** A timeout exception. + * + * Futures are failed with a timeout exception when their timeout expires. + * + * Each timeout exception contains an origin future which originally timed out. + */ + class FutureTimeoutException(origin: Future[T], message: String) extends TimeoutException(message) { + def this(origin: Future[T]) = this(origin, "Future timed out.") + } + } diff --git a/src/library/scala/package.scala b/src/library/scala/package.scala index 0c5d10b15e..915ce6a648 100644 --- a/src/library/scala/package.scala +++ b/src/library/scala/package.scala @@ -27,7 +27,8 @@ package object scala { type NoSuchElementException = java.util.NoSuchElementException type NumberFormatException = java.lang.NumberFormatException type AbstractMethodError = java.lang.AbstractMethodError - + type InterruptedException = java.lang.InterruptedException + @deprecated("instead of `@serializable class C`, use `class C extends Serializable`", "2.9.0") type serializable = annotation.serializable -- cgit v1.2.3 From 83d0ece42e4f0c5e41e3acae61e8e41db95defc5 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 14:55:49 +0100 Subject: Got the futures and execution contexts related code to compile. --- src/library/scala/concurrent/DelayedLazyVal.scala | 3 +- .../scala/concurrent/ExecutionContext.scala | 10 +- .../scala/concurrent/ForkJoinTaskImpl.scala | 103 ++++++++++++++------- src/library/scala/concurrent/Future.scala | 57 +++++++++--- src/library/scala/concurrent/Promise.scala | 4 + src/library/scala/concurrent/package.scala | 10 +- 6 files changed, 132 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/DelayedLazyVal.scala b/src/library/scala/concurrent/DelayedLazyVal.scala index e308c3b5a6..391ba7e314 100644 --- a/src/library/scala/concurrent/DelayedLazyVal.scala +++ b/src/library/scala/concurrent/DelayedLazyVal.scala @@ -8,7 +8,6 @@ package scala.concurrent -import ops.future /** A `DelayedLazyVal` is a wrapper for lengthy computations which have a * valid partially computed result. @@ -40,7 +39,7 @@ class DelayedLazyVal[T](f: () => T, body: => Unit) { */ def apply(): T = if (isDone) complete else f() - future { + ops.future { body _isDone = true } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 34c14147f5..d54b6c370e 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -12,10 +12,11 @@ trait ExecutionContext { def execute(task: Runnable): Unit - def task[T](task: () => T)(implicit timeout: Timeout): Task[T] + def task[T](task: () => T): Task[T] - def promise[T](implicit timeout: Timeout): Promise[T] + def promise[T]: Promise[T] + /** Only callable from the tasks running on the same execution context. */ def blockingCall[T](body: Blockable[T]): T } @@ -28,3 +29,8 @@ object ExecutionContext { //lazy val forBlocking = new BlockingExecutionContext } + + +sealed trait CanBlock + + diff --git a/src/library/scala/concurrent/ForkJoinTaskImpl.scala b/src/library/scala/concurrent/ForkJoinTaskImpl.scala index 6a33ca162a..bc09644aca 100644 --- a/src/library/scala/concurrent/ForkJoinTaskImpl.scala +++ b/src/library/scala/concurrent/ForkJoinTaskImpl.scala @@ -2,8 +2,9 @@ package scala.concurrent -import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } import java.util.concurrent.atomic.AtomicReferenceFieldUpdater +import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } +import scala.util.{ Timeout, Duration } import scala.annotation.tailrec @@ -13,40 +14,53 @@ import scala.annotation.tailrec * into a trait, i.e., removing its constructor parameters. */ private[concurrent] class ForkJoinTaskImpl[T](val executionContext: ForkJoinExecutionContext, val body: () => T, val timeout: Timeout) -extends FJTask[T] with Task[T] with Future[T] { +extends RecursiveAction with Task[T] with Future[T] { - private val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[ForkJoinTaskImpl[T]], classOf[FJState[T]], "state") + private val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[ForkJoinTaskImpl[T]], classOf[State[T]], "state") @volatile private var state: State[T] = _ + type Callback = Either[Throwable, T] => Any + updater.set(this, Pending(List())) private def casState(oldv: State[T], newv: State[T]) = { updater.compareAndSet(this, oldv, newv) } - @tailrec private def trySucceedState(res: T): Unit = updater.get(this) match { - case p @ Pending(cbs) => if (!casState(p, Success(res))) trySucceedState(res) - case _ => // return + @tailrec private def trySucceedState(res: T): List[Callback] = (updater.get(this): @unchecked) match { + case p @ Pending(cbs) => if (!casState(p, Success(res))) trySucceedState(res) else cbs } - @tailrec private def tryFailState(t: Throwable): Unit = updater.get(this) match { - case p @ Pending(cbs) => if (!casState(p, Failure(t))) tryFailState(t) - case _ => // return + @tailrec private def tryFailState(t: Throwable): List[Callback] = (updater.get(this): @unchecked) match { + case p @ Pending(cbs) => if (!casState(p, Failure(t))) tryFailState(t) else cbs } - // body of RecursiveTask - def compute(): T = { + private def dispatch[U](r: Runnable) = executionContext execute r + + private def processCallbacks(cbs: List[Callback], r: Either[Throwable, T]) = + for (cb <- cbs) dispatch(new Runnable { + override def run() = cb(r) + }) + + def compute(): Unit = { + var cbs: List[Callback] = null + try { val res = body() - trySucceedState(res) - } catch handledFutureException andThen { - t => tryFailState(t) - } finally tryFailState(new ExecutionException) + processCallbacks(trySucceedState(res), Right(res)) + } catch { + case t if isFutureThrowable(t) => + processCallbacks(tryFailState(t), Left(t)) + case t => + val ee = new ExecutionException(t) + processCallbacks(tryFailState(ee), Left(ee)) + throw t + } } def start(): Unit = { Thread.currentThread match { - case fj: ForkJoinWorkerThread if fj.pool eq executionContext.pool => fork() + case fj: ForkJoinWorkerThread if fj.getPool eq executionContext.pool => fork() case _ => executionContext.pool.execute(this) } } @@ -58,7 +72,7 @@ extends FJTask[T] with Task[T] with Future[T] { updater.get(this) match { case p @ Pending(lst) => val pt = p.asInstanceOf[Pending[T]] - if (casState(pt, Pending(callback :: pt.lst))) null + if (casState(pt, Pending(callback :: pt.callbacks))) null else tryAddCallback() case Success(res) => Right(res) case Failure(t) => Left(t) @@ -66,16 +80,19 @@ extends FJTask[T] with Task[T] with Future[T] { } val res = tryAddCallback() - if (res != null) dispatchTask new Runnable { + if (res != null) dispatch(new Runnable { override def run() = try callback(res) catch handledFutureException - } + }) + + this } - private def dispatchTask[U](r: Runnable) = executionContext execute r - - def isTimedout: Boolean = false // TODO + def isTimedout: Boolean = updater.get() match { + case Failure(ft: FutureTimeoutException) => true + case _ => false + } // TODO FIXME: handle timeouts def await(atMost: Duration): this.type = @@ -89,19 +106,27 @@ extends FJTask[T] with Task[T] with Future[T] { def tryCancel(): Unit = tryUnfork() + def block()(implicit canblock: CanBlock): T = { + join() + (updater.get(this): @unchecked) match { + case Success(r) => r + case Failure(t) => throw t + } + } + } -private[concurrent] sealed abstract class FJState[T] +private[concurrent] sealed abstract class State[T] -case class Pending[T](callbacks: List[Either[Throwable, T] => Any]) extends FJState[T] +case class Pending[T](callbacks: List[Either[Throwable, T] => Any]) extends State[T] -case class Success[T](result: T) extends FJState[T] +case class Success[T](result: T) extends State[T] -case class Failure[T](throwable: Throwable) extends FJState[T] +case class Failure[T](throwable: Throwable) extends State[T] private[concurrent] final class ForkJoinExecutionContext extends ExecutionContext { @@ -120,14 +145,30 @@ private[concurrent] final class ForkJoinExecutionContext extends ExecutionContex executeForkJoinTask(action) } - def makeTask[T](body: () => T)(implicit timeout: Timeout): Task[T] = { - new ForkJoinTaskImpl(this, body, timeout) + def task[T](body: () => T): Task[T] = { + new ForkJoinTaskImpl(this, body, Timeout.never) } - def makePromise[T](timeout: Timeout): Promise[T] = + def promise[T]: Promise[T] = null - def blockingCall[T](body: Blockable[T]): T = - body.block()(CanBlockEvidence) + def blockingCall[T](b: Blockable[T]): T = b match { + case fj: ForkJoinTaskImpl[_] if fj.executionContext.pool eq pool => + fj.block() + case _ => + var res: T = null.asInstanceOf[T] + @volatile var blockingDone = false + // TODO add exception handling here! + val mb = new ForkJoinPool.ManagedBlocker { + def block() = { + res = b.block()(CanBlockEvidence) + blockingDone = true + true + } + def isReleasable = blockingDone + } + ForkJoinPool.managedBlock(mb, true) + res + } } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index b65d777d67..0680e87736 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -157,23 +157,51 @@ self => /* Projections */ def failed: Future[Throwable] = new Future[Throwable] { - def newPromise[S] = self.newPromise[S] - def onComplete[U](func: Either[Throwable, Throwable] => U) = self.onComplete { - case Left(t) => func(Right(t)) - case Right(v) => // do nothing + def executionContext = self.executionContext + def onComplete[U](func: Either[Throwable, Throwable] => U) = { + self.onComplete { + case Left(t) => func(Right(t)) + case Right(v) => func(Left(noSuchElem(v))) // do nothing + } + this } def isTimedout = self.isTimedout def timeout = self.timeout + def block()(implicit canblock: CanBlock) = try { + val res = self.block() + throw noSuchElem(res) + } catch { + case t: Throwable => t + } + private def noSuchElem(v: T) = + new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v) } def timedout: Future[FutureTimeoutException] = new Future[FutureTimeoutException] { - def newPromise[S] = self.newPromise[S] - def onComplete[U](func: Either[Throwable, FutureTimeoutException] => U) = self.onComplete { - case Left(te: FutureTimeoutException) => func(Right(te)) - case _ => // do nothing + def executionContext = self.executionContext + def onComplete[U](func: Either[Throwable, FutureTimeoutException] => U) = { + self.onComplete { + case Left(te: FutureTimeoutException) => func(Right(te)) + case Left(t) => func(Left(noSuchElemThrowable(t))) + case Right(v) => func(Left(noSuchElemValue(v))) + } + this } def isTimedout = self.isTimedout def timeout = self.timeout + def block()(implicit canblock: CanBlock) = try { + val res = self.block() // TODO fix + throw noSuchElemValue(res) + } catch { + case ft: FutureTimeoutException => + ft + case t: Throwable => + throw noSuchElemThrowable(t) + } + private def noSuchElemValue(v: T) = + new NoSuchElementException("Future.timedout didn't time out. Instead completed with: " + v) + private def noSuchElemThrowable(v: Throwable) = + new NoSuchElementException("Future.timedout didn't time out. Instead failed with: " + v) } @@ -206,9 +234,9 @@ self => * * Will not be called if the future times out or fails. * - * This method typically registers an `onResult` callback. + * This method typically registers an `onSuccess` callback. */ - def foreach[U](f: T => U): Unit = onResult f + def foreach[U](f: T => U): Unit = onSuccess(f) /** Creates a new future by applying a function to the successful result of * this future. If this future is completed with an exception then the new @@ -239,8 +267,9 @@ self => onComplete { case Left(t) => p fail t - case Right(f) => f onComplete { - p fulfill _ + case Right(v) => f(v) onComplete { + case Left(t) => p fail t + case Right(v) => p fulfill v } } @@ -263,12 +292,12 @@ self => * block on h // throw a NoSuchElementException * }}} */ - def filter(p: T => Boolean): Future[T] = { + def filter(pred: T => Boolean): Future[T] = { val p = newPromise[T] onComplete { case Left(t) => p fail t - case Right(v) => if (p(v)) p fulfill v else p fail new NoSuchElementException + case Right(v) => if (pred(v)) p fulfill v else p fail new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) } p.future diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index c5336ab00f..898344cb66 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -10,6 +10,10 @@ package scala.concurrent +import scala.util.Timeout + + + /** Promise is an object which can be completed with a value or failed * with an exception. * diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 51bb1ac3e0..b9e39a21a1 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -62,14 +62,14 @@ package object concurrent { def future[T](body: =>T): Future[T] = null // TODO val handledFutureException: PartialFunction[Throwable, Throwable] = { - case t: Throwable if isFutureThrowable => t + case t: Throwable if isFutureThrowable(t) => t } // TODO rename appropriately and make public private[concurrent] def isFutureThrowable(t: Throwable) = t match { case e: Error => false case t: scala.util.control.ControlThrowable => false - case i: InterruptException => false + case i: InterruptedException => false case _ => true } @@ -78,16 +78,14 @@ package object concurrent { package concurrent { - private[concurrent] trait CanBlock - /** A timeout exception. * * Futures are failed with a timeout exception when their timeout expires. * * Each timeout exception contains an origin future which originally timed out. */ - class FutureTimeoutException(origin: Future[T], message: String) extends TimeoutException(message) { - def this(origin: Future[T]) = this(origin, "Future timed out.") + class FutureTimeoutException(origin: Future[_], message: String) extends TimeoutException(message) { + def this(origin: Future[_]) = this(origin, "Future timed out.") } } -- cgit v1.2.3 From 35b8d97cc339fe8b6499bf2d8897f9dd74ad071c Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 15:01:25 +0100 Subject: The isTimedout on fj futures fixed. --- src/library/scala/concurrent/ForkJoinTaskImpl.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ForkJoinTaskImpl.scala b/src/library/scala/concurrent/ForkJoinTaskImpl.scala index bc09644aca..faa7ecb45a 100644 --- a/src/library/scala/concurrent/ForkJoinTaskImpl.scala +++ b/src/library/scala/concurrent/ForkJoinTaskImpl.scala @@ -89,7 +89,7 @@ extends RecursiveAction with Task[T] with Future[T] { this } - def isTimedout: Boolean = updater.get() match { + def isTimedout: Boolean = updater.get(this) match { case Failure(ft: FutureTimeoutException) => true case _ => false } -- cgit v1.2.3 From 4b62e8059c1f0f8cb4624291b0aa64e6e460948e Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 15:42:16 +0100 Subject: Fixing match errors in the on* callback methods in future. --- src/library/scala/concurrent/Future.scala | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 0680e87736..eff155f5e9 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -72,13 +72,12 @@ self => * If the future has already been completed with a value, * this will either be applied immediately or be scheduled asynchronously. * - * Will not be called in case of a timeout. - * - * Will not be called in case of an exception. + * Will not be called in case of an exception (this includes the FutureTimeoutException). * * $multipleCallbacks */ def onSuccess[U](func: T => U): this.type = onComplete { + case Left(t) => // do nothing case Right(v) => func(v) } @@ -90,14 +89,15 @@ self => * If the future has already been completed with a failure, * this will either be applied immediately or be scheduled asynchronously. * - * Will not be called in case of a timeout. + * Will not be called in case that the future is completed with a value. * - * Will not be called in case of an exception. + * Will be called if the future is completed with a FutureTimeoutException. * * $multipleCallbacks */ def onFailure[U](func: Throwable => U): this.type = onComplete { case Left(t) if isFutureThrowable(t) => func(t) + case Right(v) => // do nothing } /** When this future times out, apply the provided function. @@ -107,8 +107,9 @@ self => * * $multipleCallbacks */ - def onTimeout[U](body: =>U): this.type = onComplete { - case Left(te: FutureTimeoutException) => body + def onTimeout[U](body: FutureTimeoutException => U): this.type = onComplete { + case Left(te: FutureTimeoutException) => body(te) + case Right(v) => // do nothing } /** When this future is completed, either through an exception, a timeout, or a value, -- cgit v1.2.3 From cb8e0de9538ec57a90fd71c47e20b7319c4904cc Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 16:00:48 +0100 Subject: Fixed the docs a bit. --- src/library/scala/concurrent/Future.scala | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index eff155f5e9..5f64c2fc1a 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -38,10 +38,13 @@ import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, * @define caughtThrowables * The future may contain a throwable object and this means that the future failed. * Futures obtained through combinators have the same exception as the future they were obtained from. - * The following throwable objects are treated differently: + * The following throwable objects are not caught by the future: * - Error - errors are not contained within futures * - scala.util.control.ControlException - not contained within futures * - InterruptedException - not contained within futures + * + * Instead, the future is completed with a NoSuchElementException with one of the exceptions above + * as the cause. * * @define forComprehensionExamples * Example: @@ -95,8 +98,9 @@ self => * * $multipleCallbacks */ - def onFailure[U](func: Throwable => U): this.type = onComplete { - case Left(t) if isFutureThrowable(t) => func(t) + def onFailure[U](callback: Throwable => U): this.type = onComplete { + case Left(te: FutureTimeoutException) => callback(te) + case Left(t) if isFutureThrowable(t) => callback(t) case Right(v) => // do nothing } @@ -107,8 +111,8 @@ self => * * $multipleCallbacks */ - def onTimeout[U](body: FutureTimeoutException => U): this.type = onComplete { - case Left(te: FutureTimeoutException) => body(te) + def onTimeout[U](callback: FutureTimeoutException => U): this.type = onComplete { + case Left(te: FutureTimeoutException) => callback(te) case Right(v) => // do nothing } -- cgit v1.2.3 From 4ea25c98d377b6b0369fa20aa9d4bfd3a3223ef6 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Wed, 7 Dec 2011 16:51:55 +0100 Subject: Add future method to ExecutionContext trait. Log uncaught exceptions to stderr. --- src/library/scala/concurrent/ExecutionContext.scala | 16 +++++++--------- src/library/scala/concurrent/ForkJoinTaskImpl.scala | 21 ++++++++++++++++----- src/library/scala/concurrent/package.scala | 5 +++-- 3 files changed, 26 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index d54b6c370e..9606c28bab 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -12,24 +12,22 @@ trait ExecutionContext { def execute(task: Runnable): Unit - def task[T](task: () => T): Task[T] + def task[T](task: => T): Task[T] def promise[T]: Promise[T] + def future[T](body: => T): Future[T] = { + val t = task(body) + t.start() + t.future + } + /** Only callable from the tasks running on the same execution context. */ def blockingCall[T](body: Blockable[T]): T } -object ExecutionContext { - - lazy val forNonBlocking = new ForkJoinExecutionContext - - //lazy val forBlocking = new BlockingExecutionContext - -} - sealed trait CanBlock diff --git a/src/library/scala/concurrent/ForkJoinTaskImpl.scala b/src/library/scala/concurrent/ForkJoinTaskImpl.scala index faa7ecb45a..9df4768ebb 100644 --- a/src/library/scala/concurrent/ForkJoinTaskImpl.scala +++ b/src/library/scala/concurrent/ForkJoinTaskImpl.scala @@ -13,7 +13,7 @@ import scala.annotation.tailrec * to avoid an object allocation per promise. This requires turning DefaultPromise * into a trait, i.e., removing its constructor parameters. */ -private[concurrent] class ForkJoinTaskImpl[T](val executionContext: ForkJoinExecutionContext, val body: () => T, val timeout: Timeout) +private[concurrent] class ForkJoinTaskImpl[T](val executionContext: ForkJoinExecutionContext, body: => T, val timeout: Timeout) extends RecursiveAction with Task[T] with Future[T] { private val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[ForkJoinTaskImpl[T]], classOf[State[T]], "state") @@ -46,7 +46,7 @@ extends RecursiveAction with Task[T] with Future[T] { var cbs: List[Callback] = null try { - val res = body() + val res = body processCallbacks(trySucceedState(res), Right(res)) } catch { case t if isFutureThrowable(t) => @@ -83,7 +83,9 @@ extends RecursiveAction with Task[T] with Future[T] { if (res != null) dispatch(new Runnable { override def run() = try callback(res) - catch handledFutureException + catch handledFutureException andThen { + t => Console.err.println(t) + } }) this @@ -130,7 +132,16 @@ case class Failure[T](throwable: Throwable) extends State[T] private[concurrent] final class ForkJoinExecutionContext extends ExecutionContext { - val pool = new ForkJoinPool + val pool = { + val p = new ForkJoinPool + p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler { + def uncaughtException(t: Thread, throwable: Throwable) { + Console.err.println(throwable.getMessage) + throwable.printStackTrace(Console.err) + } + }) + p + } @inline private def executeForkJoinTask(task: RecursiveAction) { @@ -145,7 +156,7 @@ private[concurrent] final class ForkJoinExecutionContext extends ExecutionContex executeForkJoinTask(action) } - def task[T](body: () => T): Task[T] = { + def task[T](body: => T): Task[T] = { new ForkJoinTaskImpl(this, body, Timeout.never) } diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index b9e39a21a1..63faeef502 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -21,6 +21,9 @@ package object concurrent { type CancellationException = java.util.concurrent.CancellationException type TimeoutException = java.util.concurrent.TimeoutException + lazy val executionContext = + new ForkJoinExecutionContext + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { override protected def initialValue = null } @@ -59,8 +62,6 @@ package object concurrent { } } - def future[T](body: =>T): Future[T] = null // TODO - val handledFutureException: PartialFunction[Throwable, Throwable] = { case t: Throwable if isFutureThrowable(t) => t } -- cgit v1.2.3 From cbad136be350087ebb29b9a36c1da893bbb18ec9 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 16:52:08 +0100 Subject: Minor changes with the docs. --- src/library/scala/concurrent/Future.scala | 22 +++++++++++++--------- src/library/scala/concurrent/Promise.scala | 13 +++++++------ 2 files changed, 20 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 5f64c2fc1a..9c1b8b4cd1 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -38,12 +38,12 @@ import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, * @define caughtThrowables * The future may contain a throwable object and this means that the future failed. * Futures obtained through combinators have the same exception as the future they were obtained from. - * The following throwable objects are not caught by the future: + * The following throwable objects are not contained in the future: * - Error - errors are not contained within futures - * - scala.util.control.ControlException - not contained within futures + * - scala.util.control.ControlThrowable - not contained within futures * - InterruptedException - not contained within futures * - * Instead, the future is completed with a NoSuchElementException with one of the exceptions above + * Instead, the future is completed with a ExecutionException with one of the exceptions above * as the cause. * * @define forComprehensionExamples @@ -59,7 +59,7 @@ import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, * }}} * * is translated to: - * + * * {{{ * f flatMap { (x: Int) => g map { (y: Int) => x + y } } * }}} @@ -85,8 +85,8 @@ self => } /** When this future is completed with a failure (i.e. with a throwable), - * apply the provided function to the throwable. - * + * apply the provided callback to the throwable. + * * $caughtThrowables * * If the future has already been completed with a failure, @@ -98,9 +98,8 @@ self => * * $multipleCallbacks */ - def onFailure[U](callback: Throwable => U): this.type = onComplete { - case Left(te: FutureTimeoutException) => callback(te) - case Left(t) if isFutureThrowable(t) => callback(t) + def onFailure[U](callback: PartialFunction[Throwable, U]): this.type = onComplete { + case Left(t) if t.isInstanceOf[FutureTimeoutException] || isFutureThrowable(t) => if (callback.isDefinedAt(t)) callback(t) case Right(v) => // do nothing } @@ -161,6 +160,9 @@ self => /* Projections */ + /** A failed projection of the future. + * + */ def failed: Future[Throwable] = new Future[Throwable] { def executionContext = self.executionContext def onComplete[U](func: Either[Throwable, Throwable] => U) = { @@ -182,6 +184,8 @@ self => new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v) } + /** A timed out projection of the future. + */ def timedout: Future[FutureTimeoutException] = new Future[FutureTimeoutException] { def executionContext = self.executionContext def onComplete[U](func: Either[Throwable, FutureTimeoutException] => U) = { diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 898344cb66..ff72dee4b9 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -17,12 +17,14 @@ import scala.util.Timeout /** Promise is an object which can be completed with a value or failed * with an exception. * - * A promise is assigned a timeout when created. After the timeout expires, - * the promise will be failed with a TimeoutException. - * * @define promiseCompletion * If the promise has already been fulfilled, failed or has timed out, * calling this method will throw an IllegalStateException. + * + * @define allowedThrowables + * If the throwable used to fail this promise is an error, a control exception + * or an interrupted exception, it will be wrapped as a cause within an + * ExecutionException which will fail the promise. */ trait Promise[T] { @@ -42,13 +44,12 @@ trait Promise[T] { * * @param t The throwable to complete the promise with. * + * $allowedThrowables + * * $promiseCompletion */ def fail(t: Throwable): Unit - /** The timeout for this promise. - */ - def timeout: Timeout } -- cgit v1.2.3 From 348bcd1f2782004ab6393e2adedb0c99131f1cda Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 7 Dec 2011 18:22:17 +0100 Subject: Some docs fixes. --- src/library/scala/concurrent/Future.scala | 32 ++++++++++++++++++++++++------ src/library/scala/concurrent/Promise.scala | 2 +- 2 files changed, 27 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 9c1b8b4cd1..19b29182bf 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -136,7 +136,7 @@ self => */ def newPromise[S]: Promise[S] = executionContext promise - /** Tests whether this Future's timeout has expired. + /** Tests whether this `Future`'s timeout has expired. * * $futureTimeout * @@ -145,13 +145,13 @@ self => */ def isTimedout: Boolean - /** This future's timeout. + /** This `Future`'s timeout. * * $futureTimeout */ def timeout: Timeout - /** This future's timeout in nanoseconds. + /** This `Future`'s timeout in nanoseconds. * * $futureTimeout */ @@ -160,8 +160,17 @@ self => /* Projections */ - /** A failed projection of the future. + /** Returns a failed projection of this future. * + * The failed projection is a future holding a value of type `Throwable`. + * + * It is completed with a value which is the throwable of the original future + * in case the original future is failed. + * + * It is failed with a `NoSuchElementException` if the original future is completed successfully. + * + * Blocking on this future returns a value if the original future is completed with an exception + * and throws a corresponding exception if the original future fails. */ def failed: Future[Throwable] = new Future[Throwable] { def executionContext = self.executionContext @@ -184,7 +193,18 @@ self => new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v) } - /** A timed out projection of the future. + /** A timed out projection of this future. + * + * The timed out projection is a future holding a value of type `FutureTimeoutException`. + * + * It is completed with a value which is a `FutureTimeoutException` of the original future + * in case the original future is timed out. + * + * It is failed with a `NoSuchElementException` if the original future is completed successfully. + * It is failed with the original exception otherwise. + * + * Blocking on this future returns a value only if the original future timed out, and a + * corresponding exception otherwise. */ def timedout: Future[FutureTimeoutException] = new Future[FutureTimeoutException] { def executionContext = self.executionContext @@ -199,7 +219,7 @@ self => def isTimedout = self.isTimedout def timeout = self.timeout def block()(implicit canblock: CanBlock) = try { - val res = self.block() // TODO fix + val res = self.block() throw noSuchElemValue(res) } catch { case ft: FutureTimeoutException => diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index ff72dee4b9..b244d8c5e0 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -24,7 +24,7 @@ import scala.util.Timeout * @define allowedThrowables * If the throwable used to fail this promise is an error, a control exception * or an interrupted exception, it will be wrapped as a cause within an - * ExecutionException which will fail the promise. + * `ExecutionException` which will fail the promise. */ trait Promise[T] { -- cgit v1.2.3 From 2c01191b99d912f99a7d50be693d09406761a94c Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Wed, 7 Dec 2011 18:24:28 +0100 Subject: Add promise implementation. Move fork/join implementation into default package. Factor commonalities of promises and tasks into Completable. --- .../scala/concurrent/ForkJoinTaskImpl.scala | 185 -------------- src/library/scala/concurrent/Future.scala | 14 -- src/library/scala/concurrent/Promise.scala | 15 +- .../scala/concurrent/default/TaskImpl.scala | 273 +++++++++++++++++++++ src/library/scala/concurrent/package.scala | 2 +- 5 files changed, 285 insertions(+), 204 deletions(-) delete mode 100644 src/library/scala/concurrent/ForkJoinTaskImpl.scala create mode 100644 src/library/scala/concurrent/default/TaskImpl.scala (limited to 'src') diff --git a/src/library/scala/concurrent/ForkJoinTaskImpl.scala b/src/library/scala/concurrent/ForkJoinTaskImpl.scala deleted file mode 100644 index 9df4768ebb..0000000000 --- a/src/library/scala/concurrent/ForkJoinTaskImpl.scala +++ /dev/null @@ -1,185 +0,0 @@ -package scala.concurrent - - - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater -import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } -import scala.util.{ Timeout, Duration } -import scala.annotation.tailrec - - - -/* DONE: The challenge is to make ForkJoinPromise inherit from RecursiveAction - * to avoid an object allocation per promise. This requires turning DefaultPromise - * into a trait, i.e., removing its constructor parameters. - */ -private[concurrent] class ForkJoinTaskImpl[T](val executionContext: ForkJoinExecutionContext, body: => T, val timeout: Timeout) -extends RecursiveAction with Task[T] with Future[T] { - - private val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[ForkJoinTaskImpl[T]], classOf[State[T]], "state") - @volatile private var state: State[T] = _ - - type Callback = Either[Throwable, T] => Any - - updater.set(this, Pending(List())) - - private def casState(oldv: State[T], newv: State[T]) = { - updater.compareAndSet(this, oldv, newv) - } - - @tailrec private def trySucceedState(res: T): List[Callback] = (updater.get(this): @unchecked) match { - case p @ Pending(cbs) => if (!casState(p, Success(res))) trySucceedState(res) else cbs - } - - @tailrec private def tryFailState(t: Throwable): List[Callback] = (updater.get(this): @unchecked) match { - case p @ Pending(cbs) => if (!casState(p, Failure(t))) tryFailState(t) else cbs - } - - private def dispatch[U](r: Runnable) = executionContext execute r - - private def processCallbacks(cbs: List[Callback], r: Either[Throwable, T]) = - for (cb <- cbs) dispatch(new Runnable { - override def run() = cb(r) - }) - - def compute(): Unit = { - var cbs: List[Callback] = null - - try { - val res = body - processCallbacks(trySucceedState(res), Right(res)) - } catch { - case t if isFutureThrowable(t) => - processCallbacks(tryFailState(t), Left(t)) - case t => - val ee = new ExecutionException(t) - processCallbacks(tryFailState(ee), Left(ee)) - throw t - } - } - - def start(): Unit = { - Thread.currentThread match { - case fj: ForkJoinWorkerThread if fj.getPool eq executionContext.pool => fork() - case _ => executionContext.pool.execute(this) - } - } - - def future: Future[T] = this - - def onComplete[U](callback: Either[Throwable, T] => U): this.type = { - @tailrec def tryAddCallback(): Either[Throwable, T] = { - updater.get(this) match { - case p @ Pending(lst) => - val pt = p.asInstanceOf[Pending[T]] - if (casState(pt, Pending(callback :: pt.callbacks))) null - else tryAddCallback() - case Success(res) => Right(res) - case Failure(t) => Left(t) - } - } - - val res = tryAddCallback() - if (res != null) dispatch(new Runnable { - override def run() = - try callback(res) - catch handledFutureException andThen { - t => Console.err.println(t) - } - }) - - this - } - - def isTimedout: Boolean = updater.get(this) match { - case Failure(ft: FutureTimeoutException) => true - case _ => false - } - - // TODO FIXME: handle timeouts - def await(atMost: Duration): this.type = - await - - def await: this.type = { - this.join() - this - } - - def tryCancel(): Unit = - tryUnfork() - - def block()(implicit canblock: CanBlock): T = { - join() - (updater.get(this): @unchecked) match { - case Success(r) => r - case Failure(t) => throw t - } - } - -} - - -private[concurrent] sealed abstract class State[T] - - -case class Pending[T](callbacks: List[Either[Throwable, T] => Any]) extends State[T] - - -case class Success[T](result: T) extends State[T] - - -case class Failure[T](throwable: Throwable) extends State[T] - - -private[concurrent] final class ForkJoinExecutionContext extends ExecutionContext { - val pool = { - val p = new ForkJoinPool - p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler { - def uncaughtException(t: Thread, throwable: Throwable) { - Console.err.println(throwable.getMessage) - throwable.printStackTrace(Console.err) - } - }) - p - } - - @inline - private def executeForkJoinTask(task: RecursiveAction) { - if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) - task.fork() - else - pool execute task - } - - def execute(task: Runnable) { - val action = new RecursiveAction { def compute() { task.run() } } - executeForkJoinTask(action) - } - - def task[T](body: => T): Task[T] = { - new ForkJoinTaskImpl(this, body, Timeout.never) - } - - def promise[T]: Promise[T] = - null - - def blockingCall[T](b: Blockable[T]): T = b match { - case fj: ForkJoinTaskImpl[_] if fj.executionContext.pool eq pool => - fj.block() - case _ => - var res: T = null.asInstanceOf[T] - @volatile var blockingDone = false - // TODO add exception handling here! - val mb = new ForkJoinPool.ManagedBlocker { - def block() = { - res = b.block()(CanBlockEvidence) - blockingDone = true - true - } - def isReleasable = blockingDone - } - ForkJoinPool.managedBlock(mb, true) - res - } - -} diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 5f64c2fc1a..8394c0b7aa 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -146,18 +146,6 @@ self => */ def isTimedout: Boolean - /** This future's timeout. - * - * $futureTimeout - */ - def timeout: Timeout - - /** This future's timeout in nanoseconds. - * - * $futureTimeout - */ - def timeoutInNanos = if (timeout.duration.isFinite) timeout.duration.toNanos else Long.MaxValue - /* Projections */ @@ -171,7 +159,6 @@ self => this } def isTimedout = self.isTimedout - def timeout = self.timeout def block()(implicit canblock: CanBlock) = try { val res = self.block() throw noSuchElem(res) @@ -193,7 +180,6 @@ self => this } def isTimedout = self.isTimedout - def timeout = self.timeout def block()(implicit canblock: CanBlock) = try { val res = self.block() // TODO fix throw noSuchElemValue(res) diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 898344cb66..33953f2fca 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -44,14 +44,21 @@ trait Promise[T] { * * $promiseCompletion */ - def fail(t: Throwable): Unit - - /** The timeout for this promise. + def break(t: Throwable): Unit + + /** Wraps a `Throwable` in an `ExecutionException` if necessary. + * + * $allowedThrowables */ - def timeout: Timeout + protected def wrap(t: Throwable): Throwable = t match { + case t: Throwable if isFutureThrowable(t) => t + case _ => new ExecutionException(t) + } + } + object Promise { /* /** diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala new file mode 100644 index 0000000000..ebb8f65ab6 --- /dev/null +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -0,0 +1,273 @@ +package scala.concurrent +package default + + + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater +import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } +import scala.util.{ Timeout, Duration } +import scala.annotation.tailrec + + + +private[concurrent] trait Completable[T] { + self: Future[T] => + + val executionContext: ExecutionContextImpl + + type Callback = Either[Throwable, T] => Any + + def getState: State[T] + + def casState(oldv: State[T], newv: State[T]): Boolean + + protected def dispatch[U](r: Runnable) = executionContext execute r + + protected def processCallbacks(cbs: List[Callback], r: Either[Throwable, T]) = + for (cb <- cbs) dispatch(new Runnable { + override def run() = cb(r) + }) + + def future: Future[T] = self + + def onComplete[U](callback: Either[Throwable, T] => U): this.type = { + @tailrec def tryAddCallback(): Either[Throwable, T] = { + getState match { + case p @ Pending(lst) => + val pt = p.asInstanceOf[Pending[T]] + if (casState(pt, Pending(callback :: pt.callbacks))) null + else tryAddCallback() + case Success(res) => Right(res) + case Failure(t) => Left(t) + } + } + + val res = tryAddCallback() + if (res != null) dispatch(new Runnable { + override def run() = + try callback(res) + catch handledFutureException andThen { + t => Console.err.println(t) + } + }) + + this + } + + def isTimedout: Boolean = getState match { + case Failure(ft: FutureTimeoutException) => true + case _ => false + } + +} + +private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) + extends Promise[T] with Future[T] with Completable[T] { + + val executionContext: scala.concurrent.default.ExecutionContextImpl = context + + @volatile private var state: State[T] = _ + + val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[PromiseImpl[T]], classOf[State[T]], "state") + + updater.set(this, Pending(List())) + + def casState(oldv: State[T], newv: State[T]): Boolean = { + updater.compareAndSet(this, oldv, newv) + } + + def getState: State[T] = { + updater.get(this) + } + + @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { + case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs + case _ => null + } + + /** Completes the promise with a value. + * + * @param value The value to complete the promise with. + * + * $promiseCompletion + */ + def fulfill(value: T): Unit = { + val cbs = tryCompleteState(Success(value)) + if (cbs == null) + throw new IllegalStateException + else { + processCallbacks(cbs, Right(value)) + this.synchronized { + this.notifyAll() + } + } + } + + /** Completes the promise with an exception. + * + * @param t The throwable to complete the promise with. + * + * $promiseCompletion + */ + def break(t: Throwable): Unit = { + val wrapped = wrap(t) + val cbs = tryCompleteState(Failure(wrapped)) + if (cbs == null) + throw new IllegalStateException + else { + processCallbacks(cbs, Left(wrapped)) + this.synchronized { + this.notifyAll() + } + } + } + + def block()(implicit canblock: scala.concurrent.CanBlock): T = getState match { + case Success(res) => res + case Failure(t) => throw t + case _ => + this.synchronized { + while (true) + getState match { + case Pending(_) => this.wait() + case Success(res) => return res + case Failure(t) => throw t + } + } + sys.error("unreachable") + } + +} + +private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) + extends RecursiveAction with Task[T] with Future[T] with Completable[T] { + + val executionContext: ExecutionContextImpl = context + + @volatile private var state: State[T] = _ + + val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[TaskImpl[T]], classOf[State[T]], "state") + + updater.set(this, Pending(List())) + + def casState(oldv: State[T], newv: State[T]): Boolean = { + updater.compareAndSet(this, oldv, newv) + } + + def getState: State[T] = { + updater.get(this) + } + + @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { + case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs + } + + def compute(): Unit = { + var cbs: List[Callback] = null + try { + val res = body + processCallbacks(tryCompleteState(Success(res)), Right(res)) + } catch { + case t if isFutureThrowable(t) => + processCallbacks(tryCompleteState(Failure(t)), Left(t)) + case t => + val ee = new ExecutionException(t) + processCallbacks(tryCompleteState(Failure(ee)), Left(ee)) + throw t + } + } + + def start(): Unit = { + Thread.currentThread match { + case fj: ForkJoinWorkerThread if fj.getPool eq executionContext.pool => fork() + case _ => executionContext.pool.execute(this) + } + } + + // TODO FIXME: handle timeouts + def await(atMost: Duration): this.type = + await + + def await: this.type = { + this.join() + this + } + + def tryCancel(): Unit = + tryUnfork() + + def block()(implicit canblock: CanBlock): T = { + join() + (updater.get(this): @unchecked) match { + case Success(r) => r + case Failure(t) => throw t + } + } + +} + + +private[concurrent] sealed abstract class State[T] + + +case class Pending[T](callbacks: List[Either[Throwable, T] => Any]) extends State[T] + + +case class Success[T](result: T) extends State[T] + + +case class Failure[T](throwable: Throwable) extends State[T] + + +private[concurrent] final class ExecutionContextImpl extends ExecutionContext { + val pool = { + val p = new ForkJoinPool + p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler { + def uncaughtException(t: Thread, throwable: Throwable) { + Console.err.println(throwable.getMessage) + throwable.printStackTrace(Console.err) + } + }) + p + } + + @inline + private def executeTask(task: RecursiveAction) { + if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) + task.fork() + else + pool execute task + } + + def execute(task: Runnable) { + val action = new RecursiveAction { def compute() { task.run() } } + executeTask(action) + } + + def task[T](body: => T): Task[T] = { + new TaskImpl(this, body) + } + + def promise[T]: Promise[T] = + null + + def blockingCall[T](b: Blockable[T]): T = b match { + case fj: TaskImpl[_] if fj.executionContext.pool eq pool => + fj.block() + case _ => + var res: T = null.asInstanceOf[T] + @volatile var blockingDone = false + // TODO add exception handling here! + val mb = new ForkJoinPool.ManagedBlocker { + def block() = { + res = b.block()(CanBlockEvidence) + blockingDone = true + true + } + def isReleasable = blockingDone + } + ForkJoinPool.managedBlock(mb, true) + res + } + +} diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 63faeef502..900e872b51 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -22,7 +22,7 @@ package object concurrent { type TimeoutException = java.util.concurrent.TimeoutException lazy val executionContext = - new ForkJoinExecutionContext + new default.ExecutionContextImpl private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { override protected def initialValue = null -- cgit v1.2.3 From f07e5e2eef9db072ce171d1df0c9133b707f2d42 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 8 Dec 2011 01:25:05 +0100 Subject: Added Future companion object, with all method. --- src/library/scala/concurrent/Future.scala | 42 +++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 325350bddf..0aa6731353 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -21,8 +21,8 @@ import scala.collection.mutable.Stack //import akka.util.Switch (commented method) import java.{ lang ⇒ jl } import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } - - +import scala.collection.mutable.Builder +import scala.collection.generic.CanBuildFrom /** The trait that represents futures. * @@ -238,7 +238,7 @@ self => val p = newPromise[U] onComplete { - case Left(t) => if (pf isDefinedAt t) p fulfill pf(t) else p fail t + case Left(t) => if (pf isDefinedAt t) p fulfill pf(t) else p break t case Right(v) => p fulfill v } @@ -263,7 +263,7 @@ self => val p = newPromise[S] onComplete { - case Left(t) => p fail t + case Left(t) => p break t case Right(v) => p fulfill f(v) } @@ -281,9 +281,9 @@ self => val p = newPromise[S] onComplete { - case Left(t) => p fail t + case Left(t) => p break t case Right(v) => f(v) onComplete { - case Left(t) => p fail t + case Left(t) => p break t case Right(v) => p fulfill v } } @@ -311,8 +311,8 @@ self => val p = newPromise[T] onComplete { - case Left(t) => p fail t - case Right(v) => if (pred(v)) p fulfill v else p fail new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) + case Left(t) => p break t + case Right(v) => if (pred(v)) p fulfill v else p break new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) } p.future @@ -320,4 +320,30 @@ self => } +object Future { + + def all[T,Coll[_] <: Traversable[_]](fs: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[Future[T]],T,Coll[T]]): Future[Coll[T]] = { + val builder = cbf(fs) + val p: Promise[Coll[T]] = executionContext.promise[Coll[T]] + + if (fs.size == 1) fs.head onComplete { + case Left(t) => p break t + case Right(v) => builder += v + p fulfill builder.result + } else { + val restFutures = all(fs.tail) + fs.head onComplete { + case Left(t) => p break t + case Right(v) => builder += v + restFuture onComplete { + case Left(t) => p break t + case Right(vs) => for (v <- vs) builder += v + p fulfill builder.result + } + } + } + p.future + + } +} -- cgit v1.2.3 From db7f44b64340a5dc24aeb28bed09241b0b902dd6 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 8 Dec 2011 15:46:11 +0100 Subject: Fixed the build. --- .../scala/concurrent/ExecutionContext.scala | 15 ++++------ src/library/scala/concurrent/Future.scala | 15 +++++----- src/library/scala/concurrent/Task.scala | 2 +- .../scala/concurrent/default/TaskImpl.scala | 6 ++++ src/library/scala/concurrent/package.scala | 34 +++++++++++++--------- 5 files changed, 41 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 9606c28bab..207e190e95 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -1,7 +1,7 @@ package scala.concurrent -import java.util.concurrent.{ Executors, Future => JFuture } +import java.util.concurrent.{ Executors, Future => JFuture, Callable } import scala.util.{ Duration, Timeout } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } @@ -10,17 +10,13 @@ trait ExecutionContext { protected implicit object CanBlockEvidence extends CanBlock - def execute(task: Runnable): Unit - - def task[T](task: => T): Task[T] + def execute(runnable: Runnable): Unit def promise[T]: Promise[T] - def future[T](body: => T): Future[T] = { - val t = task(body) - t.start() - t.future - } + def future[T](body: Callable[T]): Future[T] = future(body.call()) + + def future[T](body: => T): Future[T] /** Only callable from the tasks running on the same execution context. */ def blockingCall[T](body: Blockable[T]): T @@ -28,7 +24,6 @@ trait ExecutionContext { } - sealed trait CanBlock diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 0aa6731353..6c84ec7cd8 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -320,30 +320,31 @@ self => } + object Future { - def all[T,Coll[_] <: Traversable[_]](fs: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[Future[T]],T,Coll[T]]): Future[Coll[T]] = { - val builder = cbf(fs) + def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { + val builder = cbf(futures) val p: Promise[Coll[T]] = executionContext.promise[Coll[T]] - if (fs.size == 1) fs.head onComplete { + if (futures.size == 1) futures.head onComplete { case Left(t) => p break t case Right(v) => builder += v p fulfill builder.result } else { - val restFutures = all(fs.tail) - fs.head onComplete { + val restFutures = all(futures.tail) + futures.head onComplete { case Left(t) => p break t case Right(v) => builder += v - restFuture onComplete { + restFutures onComplete { case Left(t) => p break t case Right(vs) => for (v <- vs) builder += v p fulfill builder.result } } } + p.future - } } diff --git a/src/library/scala/concurrent/Task.scala b/src/library/scala/concurrent/Task.scala index 98c7da77d2..d6f86bac31 100644 --- a/src/library/scala/concurrent/Task.scala +++ b/src/library/scala/concurrent/Task.scala @@ -2,7 +2,7 @@ package scala.concurrent -trait Task[T] { +trait Task[+T] { def start(): Unit diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index ebb8f65ab6..ca88735266 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -248,6 +248,12 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { new TaskImpl(this, body) } + def future[T](body: => T): Future[T] = { + val t = task(body) + t.start() + t.future + } + def promise[T]: Promise[T] = null diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 900e872b51..fed7f7caf8 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -23,11 +23,31 @@ package object concurrent { lazy val executionContext = new default.ExecutionContextImpl - + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { override protected def initialValue = null } + val handledFutureException: PartialFunction[Throwable, Throwable] = { + case t: Throwable if isFutureThrowable(t) => t + } + + // TODO rename appropriately and make public + private[concurrent] def isFutureThrowable(t: Throwable) = t match { + case e: Error => false + case t: scala.util.control.ControlThrowable => false + case i: InterruptedException => false + case _ => true + } + + /* concurrency constructs */ + + def future[T](body: =>T): Future[T] = + executionContext future body + + def promise[T]: Promise[T] = + executionContext promise + /** The keyword used to block on a piece of code which potentially blocks. * * @define mayThrow @@ -62,18 +82,6 @@ package object concurrent { } } - val handledFutureException: PartialFunction[Throwable, Throwable] = { - case t: Throwable if isFutureThrowable(t) => t - } - - // TODO rename appropriately and make public - private[concurrent] def isFutureThrowable(t: Throwable) = t match { - case e: Error => false - case t: scala.util.control.ControlThrowable => false - case i: InterruptedException => false - case _ => true - } - } -- cgit v1.2.3 From 64d50018b7a17c952c2bffaa38928bee2a9ee36c Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 8 Dec 2011 23:26:33 +0100 Subject: A minor doc issue with timeouts. --- src/library/scala/concurrent/Future.scala | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 6c84ec7cd8..dc2a92ebd4 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -139,9 +139,6 @@ self => /** Tests whether this `Future`'s timeout has expired. * * $futureTimeout - * - * Note that an expired Future may still contain a value, or it may be - * completed with a value. */ def isTimedout: Boolean -- cgit v1.2.3 From c2a52307bf60ca9d8b8d4980d8594284ff320dab Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Fri, 9 Dec 2011 16:22:20 +0100 Subject: Add global scheduler with default implementation --- src/library/scala/concurrent/Scheduler.scala | 54 ++++++++++++++++++++++ .../scala/concurrent/default/SchedulerImpl.scala | 44 ++++++++++++++++++ src/library/scala/concurrent/package.scala | 14 ++++-- 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 src/library/scala/concurrent/Scheduler.scala create mode 100644 src/library/scala/concurrent/default/SchedulerImpl.scala (limited to 'src') diff --git a/src/library/scala/concurrent/Scheduler.scala b/src/library/scala/concurrent/Scheduler.scala new file mode 100644 index 0000000000..39d798e6b4 --- /dev/null +++ b/src/library/scala/concurrent/Scheduler.scala @@ -0,0 +1,54 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent + +import scala.util.Duration + +/** A service for scheduling tasks and thunks for one-time, or periodic execution. + */ +trait Scheduler { + + /** Schedules a thunk for repeated execution with an initial delay and a frequency. + * + * @param delay the initial delay after which the thunk should be executed + * the first time + * @param frequency the frequency with which the thunk should be executed, + * as a time period between subsequent executions + */ + def schedule(delay: Duration, frequency: Duration)(thunk: => Unit): Cancellable + + /** Schedules a task for execution after a given delay. + * + * @param delay the duration after which the task should be executed + * @param task the task that is scheduled for execution + * @return a `Cancellable` that may be used to cancel the execution + * of the task + */ + def scheduleOnce(delay: Duration, task: Runnable): Cancellable + + /** Schedules a thunk for execution after a given delay. + * + * @param delay the duration after which the thunk should be executed + * @param thunk the thunk that is scheduled for execution + * @return a `Cancellable` that may be used to cancel the execution + * of the thunk + */ + def scheduleOnce(delay: Duration)(task: => Unit): Cancellable + +} + + + +trait Cancellable { + + /** Cancels the underlying task. + */ + def cancel(): Unit + +} diff --git a/src/library/scala/concurrent/default/SchedulerImpl.scala b/src/library/scala/concurrent/default/SchedulerImpl.scala new file mode 100644 index 0000000000..745d2d1a15 --- /dev/null +++ b/src/library/scala/concurrent/default/SchedulerImpl.scala @@ -0,0 +1,44 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent +package default + +import scala.util.Duration + +private[concurrent] final class SchedulerImpl extends Scheduler { + private val timer = + new java.util.Timer(true) // the associated thread runs as a daemon + + def schedule(delay: Duration, frequency: Duration)(thunk: => Unit): Cancellable = ??? + + def scheduleOnce(delay: Duration, task: Runnable): Cancellable = { + val timerTask = new java.util.TimerTask { + def run(): Unit = + task.run() + } + timer.schedule(timerTask, delay.toMillis) + new Cancellable { + def cancel(): Unit = + timerTask.cancel() + } + } + + def scheduleOnce(delay: Duration)(task: => Unit): Cancellable = { + val timerTask = new java.util.TimerTask { + def run(): Unit = + task + } + timer.schedule(timerTask, delay.toMillis) + new Cancellable { + def cancel(): Unit = + timerTask.cancel() + } + } + +} diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index fed7f7caf8..a139a6d91f 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -13,17 +13,24 @@ package scala -/** This package object contains primitives for parallel programming. +/** This package object contains primitives for concurrent and parallel programming. */ package object concurrent { - type ExecutionException = java.util.concurrent.ExecutionException + type ExecutionException = java.util.concurrent.ExecutionException type CancellationException = java.util.concurrent.CancellationException - type TimeoutException = java.util.concurrent.TimeoutException + type TimeoutException = java.util.concurrent.TimeoutException + /** A global execution environment for executing lightweight tasks. + */ lazy val executionContext = new default.ExecutionContextImpl + /** A global service for scheduling tasks for execution. + */ + lazy val scheduler = + new default.SchedulerImpl + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { override protected def initialValue = null } @@ -85,6 +92,7 @@ package object concurrent { } + package concurrent { /** A timeout exception. -- cgit v1.2.3 From 7021aef3fd8a20c8f730af36f229e7bb2cfe8fb5 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 12 Dec 2011 18:15:41 +0100 Subject: Syntax changes for the scala.concurrent package and some cleanup. block on { } is now changed to: block(timeout) { } --- .../scala/concurrent/ExecutionContext.scala | 4 +- src/library/scala/concurrent/Future.scala | 19 +++----- .../scala/concurrent/default/TaskImpl.scala | 10 +++- src/library/scala/concurrent/package.scala | 56 +++++++++++----------- test/files/jvm/concurrent-future.scala | 15 ++++-- 5 files changed, 55 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 207e190e95..5d802d71b2 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -12,6 +12,8 @@ trait ExecutionContext { def execute(runnable: Runnable): Unit + def execute[U](body: () => U): Unit + def promise[T]: Promise[T] def future[T](body: Callable[T]): Future[T] = future(body.call()) @@ -19,7 +21,7 @@ trait ExecutionContext { def future[T](body: => T): Future[T] /** Only callable from the tasks running on the same execution context. */ - def blockingCall[T](body: Blockable[T]): T + def blockingCall[T](timeout: Timeout, body: Blockable[T]): T } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index dc2a92ebd4..1f44b50018 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -26,11 +26,6 @@ import scala.collection.generic.CanBuildFrom /** The trait that represents futures. * - * @define futureTimeout - * The timeout of the future is: - * - if this future was obtained from a task (i.e. by calling `task.future`), the timeout associated with that task - * - if this future was obtained from a promise (i.e. by calling `promise.future`), the timeout associated with that promise - * * @define multipleCallbacks * Multiple callbacks may be registered; there is no guarantee that they will be * executed in a particular order. @@ -75,8 +70,6 @@ self => * If the future has already been completed with a value, * this will either be applied immediately or be scheduled asynchronously. * - * Will not be called in case of an exception (this includes the FutureTimeoutException). - * * $multipleCallbacks */ def onSuccess[U](func: T => U): this.type = onComplete { @@ -94,15 +87,14 @@ self => * * Will not be called in case that the future is completed with a value. * - * Will be called if the future is completed with a FutureTimeoutException. - * * $multipleCallbacks */ def onFailure[U](callback: PartialFunction[Throwable, U]): this.type = onComplete { - case Left(t) if t.isInstanceOf[FutureTimeoutException] || isFutureThrowable(t) => if (callback.isDefinedAt(t)) callback(t) + case Left(t) if isFutureThrowable(t) => if (callback.isDefinedAt(t)) callback(t) case Right(v) => // do nothing } + /* To be removed /** When this future times out, apply the provided function. * * If the future has already timed out, @@ -114,6 +106,7 @@ self => case Left(te: FutureTimeoutException) => callback(te) case Right(v) => // do nothing } + */ /** When this future is completed, either through an exception, a timeout, or a value, * apply the provided function. @@ -136,11 +129,13 @@ self => */ def newPromise[S]: Promise[S] = executionContext promise + /* /** Tests whether this `Future`'s timeout has expired. * * $futureTimeout */ def isTimedout: Boolean + */ /* Projections */ @@ -166,7 +161,6 @@ self => } this } - def isTimedout = self.isTimedout def block()(implicit canblock: CanBlock) = try { val res = self.block() throw noSuchElem(res) @@ -177,6 +171,7 @@ self => new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v) } + /* /** A timed out projection of this future. * * The timed out projection is a future holding a value of type `FutureTimeoutException`. @@ -215,7 +210,7 @@ self => private def noSuchElemThrowable(v: Throwable) = new NoSuchElementException("Future.timedout didn't time out. Instead failed with: " + v) } - + */ /* Monadic operations */ diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index ca88735266..b4c08ba710 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -244,6 +244,11 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { executeTask(action) } + def execute[U](body: () => U) { + val action = new RecursiveAction { def compute() { body() } } + executeTask(action) + } + def task[T](body: => T): Task[T] = { new TaskImpl(this, body) } @@ -255,9 +260,10 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { } def promise[T]: Promise[T] = - null + null // TODO - def blockingCall[T](b: Blockable[T]): T = b match { + // TODO fix the timeout + def blockingCall[T](timeout: Timeout, b: Blockable[T]): T = b match { case fj: TaskImpl[_] if fj.executionContext.pool eq pool => fj.block() case _ => diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index fed7f7caf8..dbe2f90f18 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -12,6 +12,9 @@ package scala +import scala.util.{ Timeout, Duration } + + /** This package object contains primitives for parallel programming. */ @@ -42,43 +45,38 @@ package object concurrent { /* concurrency constructs */ - def future[T](body: =>T): Future[T] = - executionContext future body + def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = + execCtx future body - def promise[T]: Promise[T] = - executionContext promise + def promise[T](implicit execCtx: ExecutionContext = executionContext): Promise[T] = + execCtx promise - /** The keyword used to block on a piece of code which potentially blocks. + /** Used to block on a piece of code which potentially blocks. * - * @define mayThrow + * @param body A piece of code which contains potentially blocking or long running calls. + * + * Calling this method may throw the following exceptions: + * - CancellationException - if the computation was cancelled + * - InterruptedException - in the case that a wait within the blockable object was interrupted + * - TimeoutException - in the case that the blockable object timed out + */ + def block[T](timeout: Timeout)(body: =>T): T = block(timeout, new Blockable[T] { + def block()(implicit cb: CanBlock) = body + }) + + /** Blocks on a blockable object. + * + * @param blockable An object with a `block` method which runs potentially blocking or long running calls. + * * Calling this method may throw the following exceptions: * - CancellationException - if the computation was cancelled * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - object block { - - /** Blocks on a piece of code. - * - * @param body A piece of code which contains potentially blocking or long running calls. - * - * $mayThrow - */ - def on[T](body: =>T): T = on(new Blockable[T] { - def block()(implicit cb: CanBlock) = body - }) - - /** Blocks on a blockable object. - * - * @param blockable An object with a `block` method which runs potentially blocking or long running calls. - * - * $mayThrow - */ - def on[T](blockable: Blockable[T]): T = { - currentExecutionContext.get match { - case null => blockable.block()(null) // outside - case x => x.blockingCall(blockable) // inside an execution context thread - } + def block[T](timeout: Timeout, blockable: Blockable[T]): T = { + currentExecutionContext.get match { + case null => blockable.block()(null) // outside - TODO - fix timeout case + case x => x.blockingCall(timeout, blockable) // inside an execution context thread } } diff --git a/test/files/jvm/concurrent-future.scala b/test/files/jvm/concurrent-future.scala index 9c2f04fb07..8fb237eb0a 100644 --- a/test/files/jvm/concurrent-future.scala +++ b/test/files/jvm/concurrent-future.scala @@ -1,5 +1,9 @@ -import scala.concurrent.{ executionContext, FutureTimeoutException, ExecutionException, SyncVar } -import executionContext._ + + + +import scala.concurrent._ + + object Test extends App { @@ -60,9 +64,10 @@ object Test extends App { output(4, "onoes") done() } - f onFailure { _ => - output(4, "kthxbye") - done() + f onFailure { + case _ => + output(4, "kthxbye") + done() } } -- cgit v1.2.3 From 9a04d0bea379c3fa12227493fddd0bffde9a569c Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 12 Dec 2011 19:59:41 +0100 Subject: Change promise fulfill and break to success and failure. --- src/library/scala/concurrent/Blockable.scala | 1 + .../scala/concurrent/ExecutionContext.scala | 4 +++- src/library/scala/concurrent/Future.scala | 28 +++++++++++----------- src/library/scala/concurrent/Promise.scala | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Blockable.scala b/src/library/scala/concurrent/Blockable.scala index 1ad02c7469..d5c45a9e4e 100644 --- a/src/library/scala/concurrent/Blockable.scala +++ b/src/library/scala/concurrent/Blockable.scala @@ -21,3 +21,4 @@ trait Blockable[+T] { + diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 5d802d71b2..77d805b19c 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -1,11 +1,13 @@ package scala.concurrent + import java.util.concurrent.{ Executors, Future => JFuture, Callable } import scala.util.{ Duration, Timeout } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } + trait ExecutionContext { protected implicit object CanBlockEvidence extends CanBlock @@ -19,7 +21,7 @@ trait ExecutionContext { def future[T](body: Callable[T]): Future[T] = future(body.call()) def future[T](body: => T): Future[T] - + /** Only callable from the tasks running on the same execution context. */ def blockingCall[T](timeout: Timeout, body: Blockable[T]): T diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 1f44b50018..f653a8a47d 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -230,8 +230,8 @@ self => val p = newPromise[U] onComplete { - case Left(t) => if (pf isDefinedAt t) p fulfill pf(t) else p break t - case Right(v) => p fulfill v + case Left(t) => if (pf isDefinedAt t) p success pf(t) else p failure t + case Right(v) => p success v } p.future @@ -255,8 +255,8 @@ self => val p = newPromise[S] onComplete { - case Left(t) => p break t - case Right(v) => p fulfill f(v) + case Left(t) => p failure t + case Right(v) => p success f(v) } p.future @@ -273,10 +273,10 @@ self => val p = newPromise[S] onComplete { - case Left(t) => p break t + case Left(t) => p failure t case Right(v) => f(v) onComplete { - case Left(t) => p break t - case Right(v) => p fulfill v + case Left(t) => p failure t + case Right(v) => p success v } } @@ -303,8 +303,8 @@ self => val p = newPromise[T] onComplete { - case Left(t) => p break t - case Right(v) => if (pred(v)) p fulfill v else p break new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) + case Left(t) => p failure t + case Right(v) => if (pred(v)) p success v else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) } p.future @@ -320,18 +320,18 @@ object Future { val p: Promise[Coll[T]] = executionContext.promise[Coll[T]] if (futures.size == 1) futures.head onComplete { - case Left(t) => p break t + case Left(t) => p failure t case Right(v) => builder += v - p fulfill builder.result + p success builder.result } else { val restFutures = all(futures.tail) futures.head onComplete { - case Left(t) => p break t + case Left(t) => p failure t case Right(v) => builder += v restFutures onComplete { - case Left(t) => p break t + case Left(t) => p failure t case Right(vs) => for (v <- vs) builder += v - p fulfill builder.result + p success builder.result } } } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index fb80eb8f31..aae0135af4 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -38,7 +38,7 @@ trait Promise[T] { * * $promiseCompletion */ - def fulfill(value: T): Unit + def success(value: T): Unit /** Completes the promise with an exception. * @@ -48,7 +48,7 @@ trait Promise[T] { * * $promiseCompletion */ - def break(t: Throwable): Unit + def failure(t: Throwable): Unit /** Wraps a `Throwable` in an `ExecutionException` if necessary. * -- cgit v1.2.3 From 5fcd1e067767b56ae323b0a37b169bf4e0a937b6 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Tue, 13 Dec 2011 09:03:30 +0100 Subject: Fix default.ExecutionContextImpl.promise. Add promise test. --- .../scala/concurrent/default/TaskImpl.scala | 6 +++--- test/files/jvm/scala-concurrent-tck.scala | 25 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index b4c08ba710..ea465225c8 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -91,7 +91,7 @@ private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) * * $promiseCompletion */ - def fulfill(value: T): Unit = { + def success(value: T): Unit = { val cbs = tryCompleteState(Success(value)) if (cbs == null) throw new IllegalStateException @@ -109,7 +109,7 @@ private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) * * $promiseCompletion */ - def break(t: Throwable): Unit = { + def failure(t: Throwable): Unit = { val wrapped = wrap(t) val cbs = tryCompleteState(Failure(wrapped)) if (cbs == null) @@ -260,7 +260,7 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { } def promise[T]: Promise[T] = - null // TODO + new PromiseImpl[T](this) // TODO fix the timeout def blockingCall[T](timeout: Timeout, b: Blockable[T]): T = b match { diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index e1b8d2763c..705675ae43 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -3,6 +3,7 @@ import scala.concurrent.{ Future, + Promise, TimeoutException, SyncVar, ExecutionException @@ -14,6 +15,8 @@ trait TestBase { def future[T](body: =>T): Future[T] + def promise[T]: Promise[T] + def once(body: (() => Unit) => Unit) { val sv = new SyncVar[Boolean] body(() => sv put true) @@ -141,7 +144,25 @@ trait FutureProjections extends TestBase { trait Promises extends TestBase { - + + def testSuccess(): Unit = once { + done => + val p = promise[Int] + val f = p.future + + f.onSuccess { x => + done() + assert(x == 5) + } onFailure { case any => + done() + assert(false) + } + + p.success(5) + } + + testSuccess() + } @@ -161,6 +182,8 @@ with Exceptions def future[T](body: =>T) = scala.concurrent.future(body) + def promise[T] = scala.concurrent.promise[T] + } -- cgit v1.2.3 From 65b27aa3ab58b39a3b68999f4c6b9d3af7a81f85 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Tue, 13 Dec 2011 13:31:06 +0100 Subject: Rename block->await. Add more tck test cases. --- src/library/scala/concurrent/Awaitable.scala | 25 ++++++ src/library/scala/concurrent/Blockable.scala | 24 ------ .../scala/concurrent/ExecutionContext.scala | 2 +- src/library/scala/concurrent/Future.scala | 6 +- .../scala/concurrent/default/TaskImpl.scala | 12 +-- src/library/scala/concurrent/package.scala | 14 ++-- test/files/jvm/scala-concurrent-tck.scala | 95 ++++++++++++++++++++-- 7 files changed, 129 insertions(+), 49 deletions(-) create mode 100644 src/library/scala/concurrent/Awaitable.scala delete mode 100644 src/library/scala/concurrent/Blockable.scala (limited to 'src') diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala new file mode 100644 index 0000000000..85546718d2 --- /dev/null +++ b/src/library/scala/concurrent/Awaitable.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent + + + +import scala.annotation.implicitNotFound +import scala.util.Timeout + + + +trait Awaitable[+T] { + @implicitNotFound(msg = "Waiting must be done by calling `await(timeout) b`, where `b` is the `Awaitable` object.") + def await(timeout: Timeout)(implicit canblock: CanBlock): T +} + + + + diff --git a/src/library/scala/concurrent/Blockable.scala b/src/library/scala/concurrent/Blockable.scala deleted file mode 100644 index d5c45a9e4e..0000000000 --- a/src/library/scala/concurrent/Blockable.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent - - - -import scala.annotation.implicitNotFound - - - -trait Blockable[+T] { - @implicitNotFound(msg = "Blocking must be done by calling `block on b`, where `b` is the Blockable object.") - def block()(implicit canblock: CanBlock): T -} - - - - diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 77d805b19c..b7b3e901e6 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -23,7 +23,7 @@ trait ExecutionContext { def future[T](body: => T): Future[T] /** Only callable from the tasks running on the same execution context. */ - def blockingCall[T](timeout: Timeout, body: Blockable[T]): T + def blockingCall[T](timeout: Timeout, body: Awaitable[T]): T } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index f653a8a47d..9937d43b23 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -59,7 +59,7 @@ import scala.collection.generic.CanBuildFrom * f flatMap { (x: Int) => g map { (y: Int) => x + y } } * }}} */ -trait Future[+T] extends Blockable[T] { +trait Future[+T] extends Awaitable[T] { self => /* Callbacks */ @@ -161,8 +161,8 @@ self => } this } - def block()(implicit canblock: CanBlock) = try { - val res = self.block() + def await(timeout: Timeout)(implicit canblock: CanBlock) = try { + val res = self.await(timeout) throw noSuchElem(res) } catch { case t: Throwable => t diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index ea465225c8..dac6400b45 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -122,7 +122,7 @@ private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) } } - def block()(implicit canblock: scala.concurrent.CanBlock): T = getState match { + def await(timeout: Timeout)(implicit canblock: scala.concurrent.CanBlock): T = getState match { case Success(res) => res case Failure(t) => throw t case _ => @@ -196,8 +196,8 @@ private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) def tryCancel(): Unit = tryUnfork() - def block()(implicit canblock: CanBlock): T = { - join() + def await(timeout: Timeout)(implicit canblock: CanBlock): T = { + join() // TODO handle timeout also (updater.get(this): @unchecked) match { case Success(r) => r case Failure(t) => throw t @@ -263,16 +263,16 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { new PromiseImpl[T](this) // TODO fix the timeout - def blockingCall[T](timeout: Timeout, b: Blockable[T]): T = b match { + def blockingCall[T](timeout: Timeout, b: Awaitable[T]): T = b match { case fj: TaskImpl[_] if fj.executionContext.pool eq pool => - fj.block() + fj.await(timeout) case _ => var res: T = null.asInstanceOf[T] @volatile var blockingDone = false // TODO add exception handling here! val mb = new ForkJoinPool.ManagedBlocker { def block() = { - res = b.block()(CanBlockEvidence) + res = b.await(timeout)(CanBlockEvidence) blockingDone = true true } diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index d93d5b04ba..d9923d6d56 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -59,7 +59,7 @@ package object concurrent { execCtx promise /** Used to block on a piece of code which potentially blocks. - * + * * @param body A piece of code which contains potentially blocking or long running calls. * * Calling this method may throw the following exceptions: @@ -67,23 +67,23 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def block[T](timeout: Timeout)(body: =>T): T = block(timeout, new Blockable[T] { - def block()(implicit cb: CanBlock) = body + def await[T](timeout: Timeout)(body: =>T): T = await(timeout, new Awaitable[T] { + def await(timeout: Timeout)(implicit cb: CanBlock) = body }) /** Blocks on a blockable object. * - * @param blockable An object with a `block` method which runs potentially blocking or long running calls. + * @param awaitable An object with a `block` method which runs potentially blocking or long running calls. * * Calling this method may throw the following exceptions: * - CancellationException - if the computation was cancelled * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def block[T](timeout: Timeout, blockable: Blockable[T]): T = { + def await[T](timeout: Timeout, awaitable: Awaitable[T]): T = { currentExecutionContext.get match { - case null => blockable.block()(null) // outside - TODO - fix timeout case - case x => x.blockingCall(timeout, blockable) // inside an execution context thread + case null => awaitable.await(timeout)(null) // outside - TODO - fix timeout case + case x => x.blockingCall(timeout, awaitable) // inside an execution context thread } } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 6e291c396b..774d4236b7 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -8,15 +8,14 @@ import scala.concurrent.{ SyncVar, ExecutionException } +import scala.concurrent.future +import scala.concurrent.promise +import scala.concurrent.await trait TestBase { - def future[T](body: =>T): Future[T] - - def promise[T]: Promise[T] - def once(body: (() => Unit) => Unit) { val sv = new SyncVar[Boolean] body(() => sv put true) @@ -206,6 +205,90 @@ trait FutureCombinators extends TestBase { trait FutureProjections extends TestBase { + def testFailedFailureOnComplete(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + f.failed onComplete { + case Right(t) => + assert(t == cause) + done() + case Left(t) => + assert(false) + } + } + + def testFailedFailureOnSuccess(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + f.failed onSuccess { + t => + assert(t == cause) + done() + } + } + + def testFailedSuccessOnComplete(): Unit = once { + done => + val f = future { 0 } + f.failed onComplete { + case Right(t) => + assert(false) + case Left(t) => + assert(t.isInstanceOf[NoSuchElementException]) + done() + } + } + + def testFailedSuccessOnFailure(): Unit = once { + done => + val f = future { 0 } + f.failed onFailure { + case nsee: NoSuchElementException => + done() + } + } + + def testFailedFailureAwait(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + assert(await(0, f.failed) == cause) + done() + } + + def testFailedSuccessAwait(): Unit = once { + done => + val f = future { 0 } + try { + println(await(0, f.failed)) + assert(false) + } catch { + case nsee: NoSuchElementException => done() + } + } + + testFailedFailureOnComplete() + testFailedFailureOnSuccess() + testFailedSuccessOnComplete() + testFailedSuccessOnFailure() + testFailedFailureAwait() + //testFailedSuccessAwait() + +} + + +trait Blocking extends TestBase { + + // TODO + } @@ -246,10 +329,6 @@ with Promises with Exceptions { - def future[T](body: =>T) = scala.concurrent.future(body) - - def promise[T] = scala.concurrent.promise[T] - } -- cgit v1.2.3 From c3477895c08397234a4a103911a4b55517a440b6 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Tue, 13 Dec 2011 15:26:12 +0100 Subject: Add test cases for blocking. Fix in the failed projection. --- src/library/scala/concurrent/Future.scala | 14 +++++++++----- src/library/scala/concurrent/package.scala | 2 +- test/files/jvm/scala-concurrent-tck.scala | 30 +++++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 9937d43b23..36126056c9 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -161,11 +161,15 @@ self => } this } - def await(timeout: Timeout)(implicit canblock: CanBlock) = try { - val res = self.await(timeout) - throw noSuchElem(res) - } catch { - case t: Throwable => t + def await(timeout: Timeout)(implicit canblock: CanBlock): Throwable = { + var t: Throwable = null + try { + val res = self.await(timeout) + t = noSuchElem(res) + } catch { + case t: Throwable => return t + } + throw t } private def noSuchElem(v: T) = new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v) diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index d9923d6d56..0cdb52fb69 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -33,7 +33,7 @@ package object concurrent { */ lazy val scheduler = new default.SchedulerImpl - + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { override protected def initialValue = null } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 774d4236b7..ffe23de756 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -268,7 +268,7 @@ trait FutureProjections extends TestBase { done => val f = future { 0 } try { - println(await(0, f.failed)) + await(0, f.failed) assert(false) } catch { case nsee: NoSuchElementException => done() @@ -280,14 +280,38 @@ trait FutureProjections extends TestBase { testFailedSuccessOnComplete() testFailedSuccessOnFailure() testFailedFailureAwait() - //testFailedSuccessAwait() + testFailedSuccessAwait() } trait Blocking extends TestBase { - // TODO + def testAwaitSuccess(): Unit = once { + done => + val f = future { 0 } + await(0, f) + done() + } + + def testAwaitFailure(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + try { + await(0, f) + assert(false) + } catch { + case t => + assert(t == cause) + done() + } + } + + testAwaitSuccess() + testAwaitFailure() } -- cgit v1.2.3 From a458396b461669129d28f45e92265560a584619b Mon Sep 17 00:00:00 2001 From: aleksandar Date: Tue, 13 Dec 2011 16:53:33 +0100 Subject: Change promise method signature. --- src/library/scala/concurrent/package.scala | 2 +- test/files/jvm/scala-concurrent-tck.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 0cdb52fb69..33e1b65993 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -55,7 +55,7 @@ package object concurrent { def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = execCtx future body - def promise[T](implicit execCtx: ExecutionContext = executionContext): Promise[T] = + def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] = execCtx promise /** Used to block on a piece of code which potentially blocks. diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index ffe23de756..7d73e6cf7b 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -320,7 +320,7 @@ trait Promises extends TestBase { def testSuccess(): Unit = once { done => - val p = promise[Int] + val p = promise[Int]() val f = p.future f.onSuccess { x => -- cgit v1.2.3 From 986bbb0914bde62978b18a3dc395b45e993a5812 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Thu, 12 Jan 2012 13:05:32 +0100 Subject: Update Future.onSuccess to take a PartialFunction. It has now the same signature as in Akka. --- src/library/scala/concurrent/Future.scala | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 36126056c9..112d10263f 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -65,16 +65,17 @@ self => /* Callbacks */ /** When this future is completed successfully (i.e. with a value), - * apply the provided function to the value. + * apply the provided partial function to the value if the partial function + * is defined at that value. * * If the future has already been completed with a value, * this will either be applied immediately or be scheduled asynchronously. * * $multipleCallbacks */ - def onSuccess[U](func: T => U): this.type = onComplete { + def onSuccess[U](pf: PartialFunction[T, U]): this.type = onComplete { case Left(t) => // do nothing - case Right(v) => func(v) + case Right(v) if pf isDefinedAt v => pf(v) } /** When this future is completed with a failure (i.e. with a throwable), @@ -243,11 +244,12 @@ self => /** Asynchronously processes the value in the future once the value becomes available. * - * Will not be called if the future times out or fails. - * - * This method typically registers an `onSuccess` callback. + * Will not be called if the future fails. */ - def foreach[U](f: T => U): Unit = onSuccess(f) + def foreach[U](f: T => U): Unit = onComplete { + case Right(r) => f(r) + case Left(_) => // do nothing + } /** Creates a new future by applying a function to the successful result of * this future. If this future is completed with an exception then the new -- cgit v1.2.3 From d43d5e5e22e61061cd84ab8b69b237249a519994 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 12 Jan 2012 14:59:10 +0100 Subject: Changing trait CanBlock name to CanAwait. --- .gitignore | 4 ++++ src/library/scala/concurrent/ExecutionContext.scala | 2 +- src/library/scala/concurrent/Future.scala | 2 +- src/library/scala/concurrent/package.scala | 4 +++- 4 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/.gitignore b/.gitignore index d392f0e82c..d38fbff32c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ *.jar +build +*.*~ +*~ +.gitignore diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index b7b3e901e6..2de080a822 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -28,6 +28,6 @@ trait ExecutionContext { } -sealed trait CanBlock +sealed trait CanAwait diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 36126056c9..dff1adb45c 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -161,7 +161,7 @@ self => } this } - def await(timeout: Timeout)(implicit canblock: CanBlock): Throwable = { + def await(timeout: Timeout)(implicit canawait: CanAwait): Throwable = { var t: Throwable = null try { val res = self.await(timeout) diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 33e1b65993..61137fbc6e 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -68,7 +68,7 @@ package object concurrent { * - TimeoutException - in the case that the blockable object timed out */ def await[T](timeout: Timeout)(body: =>T): T = await(timeout, new Awaitable[T] { - def await(timeout: Timeout)(implicit cb: CanBlock) = body + def await(timeout: Timeout)(implicit cb: CanAwait) = body }) /** Blocks on a blockable object. @@ -104,3 +104,5 @@ package concurrent { } } + + -- cgit v1.2.3 From 7a6d66399eab9e29b0e4270b1c7e47c20471c91d Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 12 Jan 2012 15:41:53 +0100 Subject: Refactor blockable to awaitable on several places. --- src/library/scala/concurrent/Awaitable.scala | 2 +- src/library/scala/concurrent/ExecutionContext.scala | 2 +- src/library/scala/concurrent/Future.scala | 4 +++- src/library/scala/concurrent/default/TaskImpl.scala | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala index 85546718d2..52fd3b9516 100644 --- a/src/library/scala/concurrent/Awaitable.scala +++ b/src/library/scala/concurrent/Awaitable.scala @@ -17,7 +17,7 @@ import scala.util.Timeout trait Awaitable[+T] { @implicitNotFound(msg = "Waiting must be done by calling `await(timeout) b`, where `b` is the `Awaitable` object.") - def await(timeout: Timeout)(implicit canblock: CanBlock): T + def await(timeout: Timeout)(implicit canawait: CanAwait): T } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 2de080a822..ebeeca995e 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -10,7 +10,7 @@ import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, Recurs trait ExecutionContext { - protected implicit object CanBlockEvidence extends CanBlock + protected implicit object CanAwaitEvidence extends CanAwait def execute(runnable: Runnable): Unit diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index bb1612876c..ada6736132 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -344,5 +344,7 @@ object Future { p.future } - + + @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) + } diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index dac6400b45..52d2ea8cfb 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -122,7 +122,7 @@ private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) } } - def await(timeout: Timeout)(implicit canblock: scala.concurrent.CanBlock): T = getState match { + def await(timeout: Timeout)(implicit canawait: scala.concurrent.CanAwait): T = getState match { case Success(res) => res case Failure(t) => throw t case _ => @@ -196,7 +196,7 @@ private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) def tryCancel(): Unit = tryUnfork() - def await(timeout: Timeout)(implicit canblock: CanBlock): T = { + def await(timeout: Timeout)(implicit canawait: CanAwait): T = { join() // TODO handle timeout also (updater.get(this): @unchecked) match { case Success(r) => r @@ -272,7 +272,7 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { // TODO add exception handling here! val mb = new ForkJoinPool.ManagedBlocker { def block() = { - res = b.await(timeout)(CanBlockEvidence) + res = b.await(timeout)(CanAwaitEvidence) blockingDone = true true } -- cgit v1.2.3 From d595324efe2be1c552bad8201aaef9ce383e5c95 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 12 Jan 2012 16:20:25 +0100 Subject: Refactor await calls for awaitable objects to ready and result calls. --- src/library/scala/concurrent/Future.scala | 123 ++++++++++--------------- src/library/scala/concurrent/akka/Future.scala | 16 ++++ src/library/scala/concurrent/package.scala | 15 ++- test/files/jvm/scala-concurrent-tck.scala | 86 +++++++++-------- 4 files changed, 123 insertions(+), 117 deletions(-) create mode 100644 src/library/scala/concurrent/akka/Future.scala (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index ada6736132..748d08be9f 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -1,29 +1,32 @@ - -/** - * Copyright (C) 2009-2011 Typesafe Inc. - */ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ package scala.concurrent -//import akka.AkkaException (replaced with Exception) -//import akka.event.Logging.Error (removed all logging) + + import scala.util.{ Timeout, Duration } import scala.Option -//import akka.japi.{ Procedure, Function ⇒ JFunc, Option ⇒ JOption } (commented methods) import java.util.concurrent.{ ConcurrentLinkedQueue, TimeUnit, Callable } -import java.util.concurrent.TimeUnit.{ NANOSECONDS ⇒ NANOS, MILLISECONDS ⇒ MILLIS } -import java.lang.{ Iterable ⇒ JIterable } -import java.util.{ LinkedList ⇒ JLinkedList } +import java.util.concurrent.TimeUnit.{ NANOSECONDS => NANOS, MILLISECONDS ⇒ MILLIS } +import java.lang.{ Iterable => JIterable } +import java.util.{ LinkedList => JLinkedList } import scala.annotation.tailrec import scala.collection.mutable.Stack -//import akka.util.Switch (commented method) -import java.{ lang ⇒ jl } +import java.{ lang => jl } import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } import scala.collection.mutable.Builder import scala.collection.generic.CanBuildFrom + + /** The trait that represents futures. * * @define multipleCallbacks @@ -95,20 +98,6 @@ self => case Right(v) => // do nothing } - /* To be removed - /** When this future times out, apply the provided function. - * - * If the future has already timed out, - * this will either be applied immediately or be scheduled asynchronously. - * - * $multipleCallbacks - */ - def onTimeout[U](callback: FutureTimeoutException => U): this.type = onComplete { - case Left(te: FutureTimeoutException) => callback(te) - case Right(v) => // do nothing - } - */ - /** When this future is completed, either through an exception, a timeout, or a value, * apply the provided function. * @@ -130,14 +119,6 @@ self => */ def newPromise[S]: Promise[S] = executionContext promise - /* - /** Tests whether this `Future`'s timeout has expired. - * - * $futureTimeout - */ - def isTimedout: Boolean - */ - /* Projections */ @@ -176,46 +157,6 @@ self => new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v) } - /* - /** A timed out projection of this future. - * - * The timed out projection is a future holding a value of type `FutureTimeoutException`. - * - * It is completed with a value which is a `FutureTimeoutException` of the original future - * in case the original future is timed out. - * - * It is failed with a `NoSuchElementException` if the original future is completed successfully. - * It is failed with the original exception otherwise. - * - * Blocking on this future returns a value only if the original future timed out, and a - * corresponding exception otherwise. - */ - def timedout: Future[FutureTimeoutException] = new Future[FutureTimeoutException] { - def executionContext = self.executionContext - def onComplete[U](func: Either[Throwable, FutureTimeoutException] => U) = { - self.onComplete { - case Left(te: FutureTimeoutException) => func(Right(te)) - case Left(t) => func(Left(noSuchElemThrowable(t))) - case Right(v) => func(Left(noSuchElemValue(v))) - } - this - } - def isTimedout = self.isTimedout - def block()(implicit canblock: CanBlock) = try { - val res = self.block() - throw noSuchElemValue(res) - } catch { - case ft: FutureTimeoutException => - ft - case t: Throwable => - throw noSuchElemThrowable(t) - } - private def noSuchElemValue(v: T) = - new NoSuchElementException("Future.timedout didn't time out. Instead completed with: " + v) - private def noSuchElemThrowable(v: Throwable) = - new NoSuchElementException("Future.timedout didn't time out. Instead failed with: " + v) - } - */ /* Monadic operations */ @@ -299,7 +240,7 @@ self => * Example: * {{{ * val f = future { 5 } - * val g = g filter { _ % 2 == 1 } + * val g = f filter { _ % 2 == 1 } * val h = f filter { _ % 2 == 0 } * block on g // evaluates to 5 * block on h // throw a NoSuchElementException @@ -316,6 +257,38 @@ self => p.future } + /** Creates a new future by mapping the value of the current future if the given partial function is defined at that value. + * + * + * If the current future contains a value for which the partial function is defined, the new future will also hold that value. + * Otherwise, the resulting future will fail with a `NoSuchElementException`. + * + * If the current future fails or times out, the resulting future also fails or times out, respectively. + * + * Example: + * {{{ + * val f = future { -5 } + * val g = f collect { + * case x if x < 0 => -x + * } + * val h = f collect { + * case x if x > 0 => x * 2 + * } + * block on g // evaluates to 5 + * block on h // throw a NoSuchElementException + * }}} + */ + def collect[S](pf: PartialFunction[T, S]): Future[S] = { + val p = newPromise[S] + + onComplete { + case Left(t) => p failure t + case Right(v) => if (pf.isDefinedAt(v)) p success pf(v) else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) + } + + p.future + } + } diff --git a/src/library/scala/concurrent/akka/Future.scala b/src/library/scala/concurrent/akka/Future.scala new file mode 100644 index 0000000000..e359456736 --- /dev/null +++ b/src/library/scala/concurrent/akka/Future.scala @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent +package akka + + + + + + diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 61137fbc6e..666e12456d 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -67,9 +67,9 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def await[T](timeout: Timeout)(body: =>T): T = await(timeout, new Awaitable[T] { + def await[T](atMost: Duration)(body: =>T): T = result(new Awaitable[T] { def await(timeout: Timeout)(implicit cb: CanAwait) = body - }) + }, atMost) /** Blocks on a blockable object. * @@ -80,13 +80,18 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def await[T](timeout: Timeout, awaitable: Awaitable[T]): T = { + def result[T](awaitable: Awaitable[T], atMost: Duration): T = { currentExecutionContext.get match { - case null => awaitable.await(timeout)(null) // outside - TODO - fix timeout case - case x => x.blockingCall(timeout, awaitable) // inside an execution context thread + case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case + case x => x.blockingCall(atMost, awaitable) // inside an execution context thread } } + def ready[T](awaitable: Awaitable[T], atMost: Duration): Awaitable[T] = { + result(awaitable, atMost) + awaitable + } + } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index ccf1162e19..d62561c92d 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -11,6 +11,8 @@ import scala.concurrent.{ import scala.concurrent.future import scala.concurrent.promise import scala.concurrent.await +import scala.concurrent.result +import scala.concurrent.ready import scala.util.Duration @@ -42,9 +44,10 @@ trait FutureCallbacks extends TestBase { val f = future { x = 1 } - f onSuccess { _ => - done() - assert(x == 1) + f onSuccess { + case _ => + done() + assert(x == 1) } } @@ -54,12 +57,14 @@ trait FutureCallbacks extends TestBase { val f = future { x = 1 } - f onSuccess { _ => + f onSuccess { + case _ => assert(x == 1) x = 2 - f onSuccess { _ => - assert(x == 2) - done() + f onSuccess { + case _ => + assert(x == 2) + done() } } } @@ -70,8 +75,8 @@ trait FutureCallbacks extends TestBase { done() throw new Exception } - f onSuccess { _ => - assert(false) + f onSuccess { + case _ => assert(false) } } @@ -82,9 +87,10 @@ trait FutureCallbacks extends TestBase { x = 1 throw new Exception } - f onSuccess { _ => - done() - assert(false) + f onSuccess { + case _ => + done() + assert(false) } f onFailure { case _ => @@ -98,9 +104,10 @@ trait FutureCallbacks extends TestBase { val f = future[Unit] { throw cause } - f onSuccess { _ => - done() - assert(false) + f onSuccess { + case _ => + done() + assert(false) } f onFailure { case e: ExecutionException if (e.getCause == cause) => @@ -116,9 +123,10 @@ trait FutureCallbacks extends TestBase { val f = future[Unit] { throw new TimeoutException() } - f onSuccess { _ => - done() - assert(false) + f onSuccess { + case _ => + done() + assert(false) } f onFailure { case e: TimeoutException => @@ -195,9 +203,10 @@ trait FutureCombinators extends TestBase { } recover { case re: RuntimeException => "recovered" - } onSuccess { x => - done() - assert(x == "recovered") + } onSuccess { + case x => + done() + assert(x == "recovered") } onFailure { case any => done() assert(false) @@ -211,9 +220,10 @@ trait FutureCombinators extends TestBase { throw cause } recover { case te: TimeoutException => "timeout" - } onSuccess { x => - done() - assert(false) + } onSuccess { + case x => + done() + assert(false) } onFailure { case any => done() assert(any == cause) @@ -258,9 +268,9 @@ trait FutureProjections extends TestBase { throw cause } f.failed onSuccess { - t => - assert(t == cause) - done() + case t => + assert(t == cause) + done() } } @@ -291,7 +301,7 @@ trait FutureProjections extends TestBase { val f = future { throw cause } - assert(await(0, f.failed) == cause) + assert(result(f.failed, Duration(500, "ms")) == cause) done() } @@ -299,7 +309,7 @@ trait FutureProjections extends TestBase { done => val f = future { 0 } try { - await(0, f.failed) + ready(f.failed, Duration(0, "ms")) assert(false) } catch { case nsee: NoSuchElementException => done() @@ -321,7 +331,7 @@ trait Blocking extends TestBase { def testAwaitSuccess(): Unit = once { done => val f = future { 0 } - await(Duration(500, "ms"), f) + ready(f, Duration(500, "ms")) done() } @@ -332,7 +342,7 @@ trait Blocking extends TestBase { throw cause } try { - await(Duration(500, "ms"), f) + ready(f, Duration(500, "ms")) assert(false) } catch { case t => @@ -354,12 +364,14 @@ trait Promises extends TestBase { val p = promise[Int]() val f = p.future - f.onSuccess { x => - done() - assert(x == 5) - } onFailure { case any => - done() - assert(false) + f.onSuccess { + case x => + done() + assert(x == 5) + } onFailure { + case any => + done() + assert(false) } p.success(5) -- cgit v1.2.3 From 5d2acb2b3d6b2880ba36f039bbf98c583ce85a21 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 12 Jan 2012 19:55:50 +0100 Subject: Port of akka Future implementation in progress. --- src/library/scala/concurrent/Future.scala | 75 +++++++++-- src/library/scala/concurrent/Promise.scala | 27 +++- src/library/scala/concurrent/akka/Future.scala | 177 ++++++++++++++++++++++++- src/library/scala/concurrent/package.scala | 6 + test/files/jvm/scala-concurrent-tck.scala | 15 ++- 5 files changed, 283 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 748d08be9f..d074dbfaaa 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -61,6 +61,9 @@ import scala.collection.generic.CanBuildFrom * {{{ * f flatMap { (x: Int) => g map { (y: Int) => x + y } } * }}} + * + * @define nonDeterministic + * Note: using this method yields nondeterministic dataflow programs. */ trait Future[+T] extends Awaitable[T] { self => @@ -113,11 +116,11 @@ self => /** The execution context of the future. */ - def executionContext: ExecutionContext + def executor: ExecutionContext /** Creates a new promise. */ - def newPromise[S]: Promise[S] = executionContext promise + def newPromise[S]: Promise[S] = executor promise /* Projections */ @@ -135,7 +138,7 @@ self => * and throws a corresponding exception if the original future fails. */ def failed: Future[Throwable] = new Future[Throwable] { - def executionContext = self.executionContext + def executor = self.executor def onComplete[U](func: Either[Throwable, Throwable] => U) = { self.onComplete { case Left(t) => func(Right(t)) @@ -242,8 +245,8 @@ self => * val f = future { 5 } * val g = f filter { _ % 2 == 1 } * val h = f filter { _ % 2 == 0 } - * block on g // evaluates to 5 - * block on h // throw a NoSuchElementException + * await(0) g // evaluates to 5 + * await(0) h // throw a NoSuchElementException * }}} */ def filter(pred: T => Boolean): Future[T] = { @@ -258,7 +261,6 @@ self => } /** Creates a new future by mapping the value of the current future if the given partial function is defined at that value. - * * * If the current future contains a value for which the partial function is defined, the new future will also hold that value. * Otherwise, the resulting future will fail with a `NoSuchElementException`. @@ -274,8 +276,8 @@ self => * val h = f collect { * case x if x > 0 => x * 2 * } - * block on g // evaluates to 5 - * block on h // throw a NoSuchElementException + * await(0) g // evaluates to 5 + * await(0) h // throw a NoSuchElementException * }}} */ def collect[S](pf: PartialFunction[T, S]): Future[S] = { @@ -289,14 +291,68 @@ self => p.future } + /** Creates a new future which holds the result of this future if it was completed successfully, or, if not, + * the result of the `that` future if `that` is completed successfully. + * If both futures are failed, the resulting future holds the throwable object of the first future. + * + * Example: + * {{{ + * val f = future { sys.error("failed") } + * val g = future { 5 } + * val h = f orElse g + * await(0) h // evaluates to 5 + * }}} + */ + def orElse[U >: T](that: Future[U]): Future[U] = { + val p = newPromise[U] + + onComplete { + case Left(t) => that onComplete { + case Left(_) => p failure t + case Right(v) => p success v + } + case Right(v) => p success v + } + + p.future + } + + /** Creates a new future which holds the result of either this future or `that` future, depending on + * which future was completed first. + * + * $nonDeterministic + * + * Example: + * {{{ + * val f = future { sys.error("failed") } + * val g = future { 5 } + * val h = f orElse g + * await(0) h // evaluates to either 5 or throws a runtime exception + * }}} + */ + def or[U >: T](that: Future[U]): Future[U] = { + val p = newPromise[U] + + val completePromise: PartialFunction[Either[Throwable, T], _] = { + case Left(t) => p tryFailure t + case Right(v) => p trySuccess v + } + this onComplete completePromise + this onComplete completePromise + + p.future + } + } object Future { + /* + // TODO make more modular by encoding this within the execution context def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { val builder = cbf(futures) - val p: Promise[Coll[T]] = executionContext.promise[Coll[T]] + val p: Promise[Coll[T]] = executor.promise[Coll[T]] if (futures.size == 1) futures.head onComplete { case Left(t) => p failure t @@ -317,6 +373,7 @@ object Future { p.future } + */ @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index aae0135af4..f6ea252f73 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -25,6 +25,9 @@ import scala.util.Timeout * If the throwable used to fail this promise is an error, a control exception * or an interrupted exception, it will be wrapped as a cause within an * `ExecutionException` which will fail the promise. + * + * @define nonDeterministic + * Note: Using this method may result in non-deterministic concurrent programs. */ trait Promise[T] { @@ -38,7 +41,15 @@ trait Promise[T] { * * $promiseCompletion */ - def success(value: T): Unit + def success(v: T): this.type = if (trySuccess(v)) this else throw new IllegalStateException("Promise already completed.") + + /** Tries to complete the promise with a value. + * + * $nonDeterministic + * + * @return If the promise has already been completed returns `false`, or `true` otherwise. + */ + def trySuccess(value: T): Boolean /** Completes the promise with an exception. * @@ -48,8 +59,16 @@ trait Promise[T] { * * $promiseCompletion */ - def failure(t: Throwable): Unit - + def failure(t: Throwable): this.type = if (tryFailure(t)) this else throw new IllegalStateException("Promise already completed.") + + /** Tries to complete the promise with an exception. + * + * $nonDeterministic + * + * @return If the promise has already been completed returns `false`, or `true` otherwise. + */ + def tryFailure(t: Throwable): Boolean + /** Wraps a `Throwable` in an `ExecutionException` if necessary. * * $allowedThrowables @@ -58,7 +77,7 @@ trait Promise[T] { case t: Throwable if isFutureThrowable(t) => t case _ => new ExecutionException(t) } - + } diff --git a/src/library/scala/concurrent/akka/Future.scala b/src/library/scala/concurrent/akka/Future.scala index e359456736..2b41c0c62e 100644 --- a/src/library/scala/concurrent/akka/Future.scala +++ b/src/library/scala/concurrent/akka/Future.scala @@ -1,4 +1,4 @@ -/* __ *\ +/*/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** @@ -6,11 +6,182 @@ ** |/ ** \* */ -package scala.concurrent -package akka +package scala.concurrent.akka +sealed trait Future[+T] extends scala.concurrent.Future with Awaitable[T] { + + implicit def executor: ExecutionContext + + /** + * For use only within a Future.flow block or another compatible Delimited Continuations reset block. + * + * Returns the result of this Future without blocking, by suspending execution and storing it as a + * continuation until the result is available. + */ + def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T ⇒ Future[Any])) + + /** + * Tests whether this Future has been completed. + */ + final def isCompleted: Boolean = value.isDefined + + /** + * The contained value of this Future. Before this Future is completed + * the value will be None. After completion the value will be Some(Right(t)) + * if it contains a valid result, or Some(Left(error)) if it contains + * an exception. + */ + def value: Option[Either[Throwable, T]] + + def onComplete(func: Either[Throwable, T] => Unit): this.type + + /** + * Creates a Future that will be the result of the first completed Future of this and the Future that was passed into this. + * This is semantically the same as: Future.firstCompletedOf(Seq(this, that)) + */ + //FIXME implement as the result of any of the Futures, or if both failed, the first failure + def orElse[A >: T](that: Future[A]): Future[A] = Future.firstCompletedOf(List(this, that)) //TODO Optimize + + final def recover[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = { + val future = Promise[A]() + onComplete { + case Left(e) if pf isDefinedAt e ⇒ future.complete(try { Right(pf(e)) } catch { case x: Exception ⇒ Left(x) }) + case otherwise ⇒ future complete otherwise + } + future + } + + /** + * Creates a new Future by applying a function to the successful result of + * this Future. If this Future is completed with an exception then the new + * Future will also contain this exception. + * Example: + *
+   * val future1 = for {
+   *   a: Int    <- actor ? "Hello" // returns 5
+   *   b: String <- actor ? a       // returns "10"
+   *   c: String <- actor ? 7       // returns "14"
+   * } yield b + "-" + c
+   * 
+ */ + final def map[A](f: T ⇒ A): Future[A] = { + val future = Promise[A]() + onComplete { + case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, A]] + case Right(res) ⇒ + future complete (try { + Right(f(res)) + } catch { + case e ⇒ + logError("Future.map", e) + Left(e) + }) + } + future + } + + /** + * Creates a new Future[A] which is completed with this Future's result if + * that conforms to A's erased type or a ClassCastException otherwise. + */ + final def mapTo[A](implicit m: Manifest[A]): Future[A] = { + val fa = Promise[A]() + onComplete { + case l: Left[_, _] ⇒ fa complete l.asInstanceOf[Either[Throwable, A]] + case Right(t) ⇒ + fa complete (try { + Right(BoxedType(m.erasure).cast(t).asInstanceOf[A]) + } catch { + case e: ClassCastException ⇒ Left(e) + }) + } + fa + } + + /** + * Creates a new Future by applying a function to the successful result of + * this Future, and returns the result of the function as the new Future. + * If this Future is completed with an exception then the new Future will + * also contain this exception. + * Example: + *
+   * val future1 = for {
+   *   a: Int    <- actor ? "Hello" // returns 5
+   *   b: String <- actor ? a       // returns "10"
+   *   c: String <- actor ? 7       // returns "14"
+   * } yield b + "-" + c
+   * 
+ */ + final def flatMap[A](f: T ⇒ Future[A]): Future[A] = { + val p = Promise[A]() + + onComplete { + case l: Left[_, _] ⇒ p complete l.asInstanceOf[Either[Throwable, A]] + case Right(r) ⇒ + try { + p completeWith f(r) + } catch { + case e ⇒ + p complete Left(e) + logError("Future.flatMap", e) + } + } + p + } + + /** + * Same as onSuccess { case r => f(r) } but is also used in for-comprehensions + */ + final def foreach(f: T ⇒ Unit): Unit = onComplete { + case Right(r) ⇒ f(r) + case _ ⇒ + } + + /** + * Used by for-comprehensions + */ + final def withFilter(p: T ⇒ Boolean) = new FutureWithFilter[T](this, p) + + final class FutureWithFilter[+A](self: Future[A], p: A ⇒ Boolean) { + def foreach(f: A ⇒ Unit): Unit = self filter p foreach f + def map[B](f: A ⇒ B): Future[B] = self filter p map f + def flatMap[B](f: A ⇒ Future[B]): Future[B] = self filter p flatMap f + def withFilter(q: A ⇒ Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) + } + + /** + * Returns a new Future that will hold the successful result of this Future if it matches + * the given predicate, if it doesn't match, the resulting Future will be a failed Future + * with a MatchError, of if this Future fails, that failure will be propagated to the returned Future + */ + final def filter(pred: T ⇒ Boolean): Future[T] = { + val p = Promise[T]() + onComplete { + case l: Left[_, _] ⇒ p complete l.asInstanceOf[Either[Throwable, T]] + case r @ Right(res) ⇒ p complete (try { + if (pred(res)) r else Left(new MatchError(res)) + } catch { + case e ⇒ + logError("Future.filter", e) + Left(e) + }) + } + p + } + + protected def logError(msg: String, problem: Throwable): Unit = { + executor match { + case m: MessageDispatcher ⇒ m.prerequisites.eventStream.publish(Error(problem, msg, problem.getMessage)) + case other ⇒ problem.printStackTrace() + } + } +} + + + +*/ diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 666e12456d..c35ece5668 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -50,6 +50,12 @@ package object concurrent { case _ => true } + private[concurrent] def resolveThrowable[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { + case Left(t: scala.runtime.NonLocalReturnControl[_]) => Right(t.value.asInstanceOf[T]) + case Left(t: InterruptedException) => Left(new ExecutionException("Boxed InterruptedException", t)) + case _ => source + } + /* concurrency constructs */ def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index d62561c92d..abd363cedf 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -184,6 +184,17 @@ trait FutureCombinators extends TestBase { done() } + // collect: stub + def testCollectSuccess(): Unit = once { + done => + done() + } + + def testCollectFailure(): Unit = once { + done => + done() + } + // foreach: stub def testForeachSuccess(): Unit = once { done => @@ -229,13 +240,15 @@ trait FutureCombinators extends TestBase { assert(any == cause) } } - + testMapSuccess() testMapFailure() testFlatMapSuccess() testFlatMapFailure() testFilterSuccess() testFilterFailure() + testCollectSuccess() + testCollectFailure() testForeachSuccess() testForeachFailure() testRecoverSuccess() -- cgit v1.2.3 From f8c3f31f2fbf1544723e4cc3fe4af602dab62372 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Fri, 13 Jan 2012 17:07:49 +0100 Subject: Work in progress on porting akka promises and futures. --- src/library/scala/concurrent/Future.scala | 45 +++-- src/library/scala/concurrent/Promise.scala | 33 +++- src/library/scala/concurrent/akka/Future.scala | 181 ++++----------------- src/library/scala/concurrent/akka/Promise.scala | 63 +++++++ src/library/scala/concurrent/akka/package.scala | 36 ++++ .../scala/concurrent/default/TaskImpl.scala | 45 +++-- src/library/scala/concurrent/package.scala | 12 +- 7 files changed, 231 insertions(+), 184 deletions(-) create mode 100644 src/library/scala/concurrent/akka/Promise.scala create mode 100644 src/library/scala/concurrent/akka/package.scala (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index d074dbfaaa..4002239fc4 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -179,8 +179,10 @@ self => val p = newPromise[U] onComplete { - case Left(t) => if (pf isDefinedAt t) p success pf(t) else p failure t - case Right(v) => p success v + case Left(t) if pf isDefinedAt t => + try { p success pf(t) } + catch { case t: Throwable => p complete resolver(t) } + case otherwise => p complete otherwise } p.future @@ -206,7 +208,11 @@ self => onComplete { case Left(t) => p failure t - case Right(v) => p success f(v) + case Right(v) => + try p success f(v) + catch { + case t => p complete resolver(t) + } } p.future @@ -224,10 +230,15 @@ self => onComplete { case Left(t) => p failure t - case Right(v) => f(v) onComplete { - case Left(t) => p failure t - case Right(v) => p success v - } + case Right(v) => + try { + f(v) onComplete { + case Left(t) => p failure t + case Right(v) => p success v + } + } catch { + case t: Throwable => p complete resolver(t) + } } p.future @@ -254,7 +265,13 @@ self => onComplete { case Left(t) => p failure t - case Right(v) => if (pred(v)) p success v else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) + case Right(v) => + try { + if (pred(v)) p success v + else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) + } catch { + case t: Throwable => p complete resolver(t) + } } p.future @@ -285,7 +302,13 @@ self => onComplete { case Left(t) => p failure t - case Right(v) => if (pf.isDefinedAt(v)) p success pf(v) else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) + case Right(v) => + try { + if (pf.isDefinedAt(v)) p success pf(v) + else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) + } catch { + case t: Throwable => p complete resolver(t) + } } p.future @@ -295,6 +318,8 @@ self => * the result of the `that` future if `that` is completed successfully. * If both futures are failed, the resulting future holds the throwable object of the first future. * + * Using this method will not cause concurrent programs to become nondeterministic. + * * Example: * {{{ * val f = future { sys.error("failed") } @@ -326,7 +351,7 @@ self => * {{{ * val f = future { sys.error("failed") } * val g = future { 5 } - * val h = f orElse g + * val h = f or g * await(0) h // evaluates to either 5 or throws a runtime exception * }}} */ diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index f6ea252f73..e5557ae1c3 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -35,13 +35,42 @@ trait Promise[T] { */ def future: Future[T] + private def throwCompleted = throw new IllegalStateException("Promise already completed.") + + /** Completes the promise with either an exception or a value. + * + * @param result Either the value or the exception to complete the promise with. + * + * $promiseCompletion + */ + def complete(result: Either[Throwable, T]): this.type = if (tryComplete(result)) this else throwCompleted + + /** Tries to complete the promise with either a value or the exception. + * + * $nonDeterministic + * + * @return If the promise has already been completed returns `false`, or `true` otherwise. + */ + def tryComplete(result: Either[Throwable, T]): Boolean + + /** Completes this promise with the specified future, once that future is completed. + * + * @return This promise + */ + final def completeWith(other: Future[T]): this.type = { + other onComplete { + this complete _ + } + this + } + /** Completes the promise with a value. * * @param value The value to complete the promise with. * * $promiseCompletion */ - def success(v: T): this.type = if (trySuccess(v)) this else throw new IllegalStateException("Promise already completed.") + def success(v: T): this.type = if (trySuccess(v)) this else throwCompleted /** Tries to complete the promise with a value. * @@ -59,7 +88,7 @@ trait Promise[T] { * * $promiseCompletion */ - def failure(t: Throwable): this.type = if (tryFailure(t)) this else throw new IllegalStateException("Promise already completed.") + def failure(t: Throwable): this.type = if (tryFailure(t)) this else throwCompleted /** Tries to complete the promise with an exception. * diff --git a/src/library/scala/concurrent/akka/Future.scala b/src/library/scala/concurrent/akka/Future.scala index 2b41c0c62e..c48009554c 100644 --- a/src/library/scala/concurrent/akka/Future.scala +++ b/src/library/scala/concurrent/akka/Future.scala @@ -1,4 +1,4 @@ -/*/* __ *\ +/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** @@ -10,178 +10,67 @@ package scala.concurrent.akka +import scala.concurrent.{Awaitable, ExecutionContext} +import scala.util.continuations._ -sealed trait Future[+T] extends scala.concurrent.Future with Awaitable[T] { +trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { implicit def executor: ExecutionContext - /** - * For use only within a Future.flow block or another compatible Delimited Continuations reset block. - * - * Returns the result of this Future without blocking, by suspending execution and storing it as a - * continuation until the result is available. + /** For use only within a Future.flow block or another compatible Delimited Continuations reset block. + * + * Returns the result of this Future without blocking, by suspending execution and storing it as a + * continuation until the result is available. */ - def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T ⇒ Future[Any])) + def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T => Future[Any])) - /** - * Tests whether this Future has been completed. + /** Tests whether this Future has been completed. */ final def isCompleted: Boolean = value.isDefined - /** - * The contained value of this Future. Before this Future is completed - * the value will be None. After completion the value will be Some(Right(t)) - * if it contains a valid result, or Some(Left(error)) if it contains - * an exception. + /** The contained value of this Future. Before this Future is completed + * the value will be None. After completion the value will be Some(Right(t)) + * if it contains a valid result, or Some(Left(error)) if it contains + * an exception. */ def value: Option[Either[Throwable, T]] - def onComplete(func: Either[Throwable, T] => Unit): this.type + def onComplete[U](func: Either[Throwable, T] => U): this.type - /** - * Creates a Future that will be the result of the first completed Future of this and the Future that was passed into this. - * This is semantically the same as: Future.firstCompletedOf(Seq(this, that)) + /** Creates a new Future[A] which is completed with this Future's result if + * that conforms to A's erased type or a ClassCastException otherwise. */ - //FIXME implement as the result of any of the Futures, or if both failed, the first failure - def orElse[A >: T](that: Future[A]): Future[A] = Future.firstCompletedOf(List(this, that)) //TODO Optimize - - final def recover[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = { - val future = Promise[A]() + final def mapTo[T](implicit m: Manifest[T]) = { + val p = executor.promise[T] + onComplete { - case Left(e) if pf isDefinedAt e ⇒ future.complete(try { Right(pf(e)) } catch { case x: Exception ⇒ Left(x) }) - case otherwise ⇒ future complete otherwise - } - future - } - - /** - * Creates a new Future by applying a function to the successful result of - * this Future. If this Future is completed with an exception then the new - * Future will also contain this exception. - * Example: - *
-   * val future1 = for {
-   *   a: Int    <- actor ? "Hello" // returns 5
-   *   b: String <- actor ? a       // returns "10"
-   *   c: String <- actor ? 7       // returns "14"
-   * } yield b + "-" + c
-   * 
- */ - final def map[A](f: T ⇒ A): Future[A] = { - val future = Promise[A]() - onComplete { - case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, A]] - case Right(res) ⇒ - future complete (try { - Right(f(res)) - } catch { - case e ⇒ - logError("Future.map", e) - Left(e) - }) - } - future - } - - /** - * Creates a new Future[A] which is completed with this Future's result if - * that conforms to A's erased type or a ClassCastException otherwise. - */ - final def mapTo[A](implicit m: Manifest[A]): Future[A] = { - val fa = Promise[A]() - onComplete { - case l: Left[_, _] ⇒ fa complete l.asInstanceOf[Either[Throwable, A]] - case Right(t) ⇒ - fa complete (try { - Right(BoxedType(m.erasure).cast(t).asInstanceOf[A]) + case l @ Left(t) => p complete l.asInstanceOf[Either[Throwable, T]] + case Right(v) => + p complete (try { + Right(boxedType(m.erasure).cast(v).asInstanceOf[T]) } catch { case e: ClassCastException ⇒ Left(e) }) } - fa + + p.future } - /** - * Creates a new Future by applying a function to the successful result of - * this Future, and returns the result of the function as the new Future. - * If this Future is completed with an exception then the new Future will - * also contain this exception. - * Example: - *
-   * val future1 = for {
-   *   a: Int    <- actor ? "Hello" // returns 5
-   *   b: String <- actor ? a       // returns "10"
-   *   c: String <- actor ? 7       // returns "14"
-   * } yield b + "-" + c
-   * 
+ /** Used by for-comprehensions. */ - final def flatMap[A](f: T ⇒ Future[A]): Future[A] = { - val p = Promise[A]() + final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p) - onComplete { - case l: Left[_, _] ⇒ p complete l.asInstanceOf[Either[Throwable, A]] - case Right(r) ⇒ - try { - p completeWith f(r) - } catch { - case e ⇒ - p complete Left(e) - logError("Future.flatMap", e) - } - } - p - } - - /** - * Same as onSuccess { case r => f(r) } but is also used in for-comprehensions - */ - final def foreach(f: T ⇒ Unit): Unit = onComplete { - case Right(r) ⇒ f(r) - case _ ⇒ - } - - /** - * Used by for-comprehensions - */ - final def withFilter(p: T ⇒ Boolean) = new FutureWithFilter[T](this, p) - - final class FutureWithFilter[+A](self: Future[A], p: A ⇒ Boolean) { - def foreach(f: A ⇒ Unit): Unit = self filter p foreach f - def map[B](f: A ⇒ B): Future[B] = self filter p map f - def flatMap[B](f: A ⇒ Future[B]): Future[B] = self filter p flatMap f - def withFilter(q: A ⇒ Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) - } - - /** - * Returns a new Future that will hold the successful result of this Future if it matches - * the given predicate, if it doesn't match, the resulting Future will be a failed Future - * with a MatchError, of if this Future fails, that failure will be propagated to the returned Future - */ - final def filter(pred: T ⇒ Boolean): Future[T] = { - val p = Promise[T]() - onComplete { - case l: Left[_, _] ⇒ p complete l.asInstanceOf[Either[Throwable, T]] - case r @ Right(res) ⇒ p complete (try { - if (pred(res)) r else Left(new MatchError(res)) - } catch { - case e ⇒ - logError("Future.filter", e) - Left(e) - }) - } - p - } - - protected def logError(msg: String, problem: Throwable): Unit = { - executor match { - case m: MessageDispatcher ⇒ m.prerequisites.eventStream.publish(Error(problem, msg, problem.getMessage)) - case other ⇒ problem.printStackTrace() - } + final class FutureWithFilter[+A](self: Future[A], p: A => Boolean) { + def foreach(f: A => Unit): Unit = self filter p foreach f + def map[B](f: A => B) = self filter p map f + def flatMap[B](f: A => Future[B]) = self filter p flatMap f + def withFilter(q: A => Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) } + } -*/ + diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala new file mode 100644 index 0000000000..a47dee48e2 --- /dev/null +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -0,0 +1,63 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent.akka + + + +import scala.concurrent.{ExecutionContext, resolver} +import scala.util.continuations._ + + + +trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { + + // TODO refine answer and return types here from Any to type parameters + + final def <<(value: T): Future[T] @cps[Future[Any]] = shift { + cont: (Future[T] => Future[Any]) => + cont(complete(Right(value))) + } + + final def <<(other: Future[T]): Future[T] @cps[Future[Any]] = shift { + cont: (Future[T] => Future[Any]) => + val p = executor.promise[Any] + val thisPromise = this + + thisPromise completeWith other + thisPromise onComplete { v => + try { + p completeWith cont(thisPromise) + } catch { + case e => p complete resolver(e) + } + } + + p.future + } + + // TODO finish this once we introduce something like dataflow streams + + /* + final def <<(stream: PromiseStreamOut[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => + val fr = executor.promise[Any] + val f = stream.dequeue(this) + f.onComplete { _ => + try { + fr completeWith cont(f) + } catch { + case e => + fr failure e + } + } + fr + } + */ + +} + diff --git a/src/library/scala/concurrent/akka/package.scala b/src/library/scala/concurrent/akka/package.scala new file mode 100644 index 0000000000..59eda5a3b4 --- /dev/null +++ b/src/library/scala/concurrent/akka/package.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent + + + +import java.{lang => jl} + + + +package object akka { + + private val toBoxed = Map[Class[_], Class[_]]( + classOf[Boolean] -> classOf[jl.Boolean], + classOf[Byte] -> classOf[jl.Byte], + classOf[Char] -> classOf[jl.Character], + classOf[Short] -> classOf[jl.Short], + classOf[Int] -> classOf[jl.Integer], + classOf[Long] -> classOf[jl.Long], + classOf[Float] -> classOf[jl.Float], + classOf[Double] -> classOf[jl.Double], + classOf[Unit] -> classOf[scala.runtime.BoxedUnit]) + + def boxedType(c: Class[_]): Class[_] = { + if (c.isPrimitive) toBoxed(c) else c + } + +} + + diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 52d2ea8cfb..716b9c02f1 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -11,9 +11,9 @@ import scala.annotation.tailrec private[concurrent] trait Completable[T] { - self: Future[T] => +self: Future[T] => - val executionContext: ExecutionContextImpl + val executor: ExecutionContextImpl type Callback = Either[Throwable, T] => Any @@ -62,9 +62,9 @@ private[concurrent] trait Completable[T] { } private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) - extends Promise[T] with Future[T] with Completable[T] { +extends Promise[T] with Future[T] with Completable[T] { - val executionContext: scala.concurrent.default.ExecutionContextImpl = context + val executor: scala.concurrent.default.ExecutionContextImpl = context @volatile private var state: State[T] = _ @@ -85,40 +85,35 @@ private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) case _ => null } - /** Completes the promise with a value. - * - * @param value The value to complete the promise with. - * - * $promiseCompletion - */ - def success(value: T): Unit = { + def tryComplete(r: Either[Throwable, T]) = r match { + case Left(t) => tryFailure(t) + case Right(v) => trySuccess(v) + } + + def trySuccess(value: T): Boolean = { val cbs = tryCompleteState(Success(value)) if (cbs == null) - throw new IllegalStateException + false else { processCallbacks(cbs, Right(value)) this.synchronized { this.notifyAll() } + true } } - /** Completes the promise with an exception. - * - * @param t The throwable to complete the promise with. - * - * $promiseCompletion - */ - def failure(t: Throwable): Unit = { + def tryFailure(t: Throwable): Boolean = { val wrapped = wrap(t) val cbs = tryCompleteState(Failure(wrapped)) if (cbs == null) - throw new IllegalStateException + false else { processCallbacks(cbs, Left(wrapped)) this.synchronized { this.notifyAll() } + true } } @@ -140,9 +135,9 @@ private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) } private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) - extends RecursiveAction with Task[T] with Future[T] with Completable[T] { +extends RecursiveAction with Task[T] with Future[T] with Completable[T] { - val executionContext: ExecutionContextImpl = context + val executor: ExecutionContextImpl = context @volatile private var state: State[T] = _ @@ -179,8 +174,8 @@ private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) def start(): Unit = { Thread.currentThread match { - case fj: ForkJoinWorkerThread if fj.getPool eq executionContext.pool => fork() - case _ => executionContext.pool.execute(this) + case fj: ForkJoinWorkerThread if fj.getPool eq executor.pool => fork() + case _ => executor.pool.execute(this) } } @@ -264,7 +259,7 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { // TODO fix the timeout def blockingCall[T](timeout: Timeout, b: Awaitable[T]): T = b match { - case fj: TaskImpl[_] if fj.executionContext.pool eq pool => + case fj: TaskImpl[_] if fj.executor.pool eq pool => fj.await(timeout) case _ => var res: T = null.asInstanceOf[T] diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index c35ece5668..ce22c53c72 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -50,12 +50,22 @@ package object concurrent { case _ => true } - private[concurrent] def resolveThrowable[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { + private[concurrent] def resolve[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { case Left(t: scala.runtime.NonLocalReturnControl[_]) => Right(t.value.asInstanceOf[T]) case Left(t: InterruptedException) => Left(new ExecutionException("Boxed InterruptedException", t)) + case Left(e: Error) => throw e case _ => source } + private val resolverFunction: PartialFunction[Throwable, Either[Throwable, _]] = { + case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value) + case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) + case e: Error => throw e + case t => Left(t) + } + + private[concurrent] def resolver[T] = resolverFunction.asInstanceOf[PartialFunction[Throwable, Either[Throwable, T]]] + /* concurrency constructs */ def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = -- cgit v1.2.3 From 7993ec04baf28cd12009d15979c2c904afad89d3 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Fri, 13 Jan 2012 19:32:48 +0100 Subject: Migrate akka promises. Changes to some of the interfaces. --- src/library/scala/concurrent/Awaitable.scala | 7 +- .../scala/concurrent/ExecutionContext.scala | 2 +- src/library/scala/concurrent/Future.scala | 24 ++- src/library/scala/concurrent/Promise.scala | 18 +-- .../scala/concurrent/akka/AbstractPromise.java | 21 +++ src/library/scala/concurrent/akka/Promise.scala | 165 ++++++++++++++++++++- src/library/scala/concurrent/akka/package.scala | 3 + .../scala/concurrent/default/TaskImpl.scala | 17 +-- src/library/scala/concurrent/package.scala | 32 ++-- test/files/jvm/scala-concurrent-tck.scala | 12 +- 10 files changed, 250 insertions(+), 51 deletions(-) create mode 100644 src/library/scala/concurrent/akka/AbstractPromise.java (limited to 'src') diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala index 52fd3b9516..c38e668f30 100644 --- a/src/library/scala/concurrent/Awaitable.scala +++ b/src/library/scala/concurrent/Awaitable.scala @@ -11,15 +11,14 @@ package scala.concurrent import scala.annotation.implicitNotFound -import scala.util.Timeout +import scala.util.Duration trait Awaitable[+T] { - @implicitNotFound(msg = "Waiting must be done by calling `await(timeout) b`, where `b` is the `Awaitable` object.") - def await(timeout: Timeout)(implicit canawait: CanAwait): T + @implicitNotFound(msg = "Waiting must be done by calling `blocking(timeout) b`, where `b` is the `Awaitable` object or a potentially blocking piece of code.") + def await(atMost: Duration)(implicit canawait: CanAwait): T } - diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index ebeeca995e..38a28044e1 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -23,7 +23,7 @@ trait ExecutionContext { def future[T](body: => T): Future[T] /** Only callable from the tasks running on the same execution context. */ - def blockingCall[T](timeout: Timeout, body: Awaitable[T]): T + def blockingCall[T](body: Awaitable[T]): T } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 4002239fc4..e6edaea87a 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -28,6 +28,18 @@ import scala.collection.generic.CanBuildFrom /** The trait that represents futures. + * + * Asynchronous computations that yield futures are created with the `future` call: + * + * {{{ + * val s = "Hello" + * val f: Future[String] = future { + * s + " future!" + * } + * f onSuccess { + * case msg => println(msg) + * } + * }}} * * @define multipleCallbacks * Multiple callbacks may be registered; there is no guarantee that they will be @@ -37,12 +49,14 @@ import scala.collection.generic.CanBuildFrom * The future may contain a throwable object and this means that the future failed. * Futures obtained through combinators have the same exception as the future they were obtained from. * The following throwable objects are not contained in the future: - * - Error - errors are not contained within futures - * - scala.util.control.ControlThrowable - not contained within futures - * - InterruptedException - not contained within futures + * - `Error` - errors are not contained within futures + * - `InterruptedException` - not contained within futures + * - all `scala.util.control.ControlThrowable` except `NonLocalReturnControl` - not contained within futures * * Instead, the future is completed with a ExecutionException with one of the exceptions above * as the cause. + * If a future is failed with a `scala.runtime.NonLocalReturnControl`, + * it is completed with a value instead from that throwable instead instead. * * @define forComprehensionExamples * Example: @@ -146,10 +160,10 @@ self => } this } - def await(timeout: Timeout)(implicit canawait: CanAwait): Throwable = { + def await(atMost: Duration)(implicit canawait: CanAwait): Throwable = { var t: Throwable = null try { - val res = self.await(timeout) + val res = self.await(atMost) t = noSuchElem(res) } catch { case t: Throwable => return t diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index e5557ae1c3..c3fa92053b 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -78,7 +78,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def trySuccess(value: T): Boolean + def trySuccess(value: T): Boolean = tryComplete(Right(value)) /** Completes the promise with an exception. * @@ -96,7 +96,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryFailure(t: Throwable): Boolean + def tryFailure(t: Throwable): Boolean = tryComplete(Left(t)) /** Wraps a `Throwable` in an `ExecutionException` if necessary. * @@ -112,15 +112,7 @@ trait Promise[T] { object Promise { - /* - /** - * Creates a non-completed, new, Promise with the supplied timeout in milliseconds - */ - def apply[A](timeout: Timeout)(implicit dispatcher: MessageDispatcher): Promise[A] = DefaultPromise[A](timeout) - - /** - * Creates a non-completed, new, Promise with the default timeout (akka.actor.timeout in conf) - */ - def apply[A]()(implicit dispatcher: MessageDispatcher, timeout: Timeout): Promise[A] = apply(timeout) - */ + + + } diff --git a/src/library/scala/concurrent/akka/AbstractPromise.java b/src/library/scala/concurrent/akka/AbstractPromise.java new file mode 100644 index 0000000000..38c74edf2f --- /dev/null +++ b/src/library/scala/concurrent/akka/AbstractPromise.java @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent.akka; + + + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + + + +abstract class AbstractPromise { + private volatile Object _ref = null; + protected final static AtomicReferenceFieldUpdater updater = + AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); +} diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index a47dee48e2..d3b93b9573 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -10,14 +10,19 @@ package scala.concurrent.akka -import scala.concurrent.{ExecutionContext, resolver} +import java.util.concurrent.TimeUnit.{ NANOSECONDS, MILLISECONDS } +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater +import scala.concurrent.{Awaitable, ExecutionContext, resolver, blocking, CanAwait, TimeoutException} import scala.util.continuations._ +import scala.util.Duration +import scala.annotation.tailrec trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { // TODO refine answer and return types here from Any to type parameters + // then move this up in the hierarchy final def <<(value: T): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => @@ -61,3 +66,161 @@ trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { } + +object Promise { + + def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] + + /** Represents the internal state. + */ + sealed trait FState[+T] { def value: Option[Either[Throwable, T]] } + + case class Pending[T](listeners: List[Either[Throwable, T] ⇒ Unit] = Nil) extends FState[T] { + def value: Option[Either[Throwable, T]] = None + } + + case class Success[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def result: T = value.get.right.get + } + + case class Failure[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def exception: Throwable = value.get.left.get + } + + private val emptyPendingValue = Pending[Nothing](Nil) + + /* default promise implementation */ + abstract class DefaultPromise[T](implicit val executor: ExecutionContext) extends AbstractPromise with Promise[T] { + self => + + updater.set(this, Promise.EmptyPending()) + + protected final def tryAwait(atMost: Duration): Boolean = { + @tailrec + def awaitUnsafe(waitTimeNanos: Long): Boolean = { + if (value.isEmpty && waitTimeNanos > 0) { + val ms = NANOSECONDS.toMillis(waitTimeNanos) + val ns = (waitTimeNanos % 1000000l).toInt // as per object.wait spec + val start = System.nanoTime() + try { synchronized { if (value.isEmpty) wait(ms, ns) } } catch { case e: InterruptedException ⇒ } + + awaitUnsafe(waitTimeNanos - (System.nanoTime() - start)) + } else + value.isDefined + } + + executor.blockingCall(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost)))) + } + + private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = + if (value.isDefined || tryAwait(atMost)) this + else throw new TimeoutException("Futures timed out after [" + atMost.toMillis + "] milliseconds") + + def await(atMost: Duration)(implicit permit: CanAwait): T = + ready(atMost).value.get match { + case Left(e) => throw e + case Right(r) => r + } + + def value: Option[Either[Throwable, T]] = getState.value + + @inline + private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]] + + @inline + protected final def updateState(oldState: FState[T], newState: FState[T]): Boolean = updater.compareAndSet(this, oldState, newState) + + @inline + protected final def getState: FState[T] = updater.get(this) + + /* + def tryComplete(value: Either[Throwable, T]): Boolean = { + val callbacks: List[Either[Throwable, T] => Unit] = { + try { + @tailrec + def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Unit] = { + getState match { + case cur @ Pending(listeners) => + if (updateState(cur, if (v.isLeft) Failure(Some(v)) else Success(Some(v)))) listeners + else tryComplete(v) + case _ => null + } + } + tryComplete(resolve(value)) + } finally { + synchronized { notifyAll() } //Notify any evil blockers + } + } + + callbacks match { + case null => false + case cs if cs.isEmpty => true + case cs => Future.dispatchTask(() => cs.foreach(f => notifyCompleted(f, value))); true + } + } + + def onComplete(func: Either[Throwable, T] => Unit): this.type = { + @tailrec //Returns whether the future has already been completed or not + def tryAddCallback(): Boolean = { + val cur = getState + cur match { + case _: Success[_] | _: Failure[_] => true + case p: Pending[_] => + val pt = p.asInstanceOf[Pending[T]] + if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false else tryAddCallback() + } + } + + if (tryAddCallback()) { + val result = value.get + Future.dispatchTask(() => notifyCompleted(func, result)) + } + + this + } + + private final def notifyCompleted(func: Either[Throwable, T] => Unit, result: Either[Throwable, T]) { + try { func(result) } catch { case e => logError("Future onComplete-callback raised an exception", e) } + } + */ + } + + /* + /** + * An already completed Future is seeded with it's result at creation, is useful for when you are participating in + * a Future-composition but you already have a value to contribute. + */ + final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContext) extends Promise[T] { + val value = Some(resolve(suppliedValue)) + + def tryComplete(value: Either[Throwable, T]): Boolean = false + def onComplete(func: Either[Throwable, T] => Unit): this.type = { + val completedAs = value.get + Future dispatchTask (() => func(completedAs)) + this + } + + def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this + def result(atMost: Duration)(implicit permit: CanAwait): T = value.get match { + case Left(e) => throw e + case Right(r) => r + } + } + */ +} + + + + + + + + + + + + + + + + diff --git a/src/library/scala/concurrent/akka/package.scala b/src/library/scala/concurrent/akka/package.scala index 59eda5a3b4..8c059b8e71 100644 --- a/src/library/scala/concurrent/akka/package.scala +++ b/src/library/scala/concurrent/akka/package.scala @@ -11,6 +11,7 @@ package scala.concurrent import java.{lang => jl} +import scala.util.Duration @@ -31,6 +32,8 @@ package object akka { if (c.isPrimitive) toBoxed(c) else c } + def dur2long(dur: Duration): Long = if (dur.isFinite) dur.toNanos else Long.MaxValue + } diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 716b9c02f1..a38541df5d 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -5,7 +5,7 @@ package default import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } -import scala.util.{ Timeout, Duration } +import scala.util.Duration import scala.annotation.tailrec @@ -90,7 +90,7 @@ extends Promise[T] with Future[T] with Completable[T] { case Right(v) => trySuccess(v) } - def trySuccess(value: T): Boolean = { + override def trySuccess(value: T): Boolean = { val cbs = tryCompleteState(Success(value)) if (cbs == null) false @@ -103,7 +103,7 @@ extends Promise[T] with Future[T] with Completable[T] { } } - def tryFailure(t: Throwable): Boolean = { + override def tryFailure(t: Throwable): Boolean = { val wrapped = wrap(t) val cbs = tryCompleteState(Failure(wrapped)) if (cbs == null) @@ -117,7 +117,7 @@ extends Promise[T] with Future[T] with Completable[T] { } } - def await(timeout: Timeout)(implicit canawait: scala.concurrent.CanAwait): T = getState match { + def await(atMost: Duration)(implicit canawait: scala.concurrent.CanAwait): T = getState match { case Success(res) => res case Failure(t) => throw t case _ => @@ -191,7 +191,7 @@ extends RecursiveAction with Task[T] with Future[T] with Completable[T] { def tryCancel(): Unit = tryUnfork() - def await(timeout: Timeout)(implicit canawait: CanAwait): T = { + def await(atMost: Duration)(implicit canawait: CanAwait): T = { join() // TODO handle timeout also (updater.get(this): @unchecked) match { case Success(r) => r @@ -257,17 +257,16 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { def promise[T]: Promise[T] = new PromiseImpl[T](this) - // TODO fix the timeout - def blockingCall[T](timeout: Timeout, b: Awaitable[T]): T = b match { + def blockingCall[T](b: Awaitable[T]): T = b match { case fj: TaskImpl[_] if fj.executor.pool eq pool => - fj.await(timeout) + fj.await(Duration.fromNanos(0)) case _ => var res: T = null.asInstanceOf[T] @volatile var blockingDone = false // TODO add exception handling here! val mb = new ForkJoinPool.ManagedBlocker { def block() = { - res = b.await(timeout)(CanAwaitEvidence) + res = b.await(Duration.fromNanos(0))(CanAwaitEvidence) blockingDone = true true } diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index ce22c53c72..7552100af2 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -52,15 +52,17 @@ package object concurrent { private[concurrent] def resolve[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { case Left(t: scala.runtime.NonLocalReturnControl[_]) => Right(t.value.asInstanceOf[T]) + case Left(t: scala.util.control.ControlThrowable) => Left(new ExecutionException("Boxed ControlThrowable", t)) case Left(t: InterruptedException) => Left(new ExecutionException("Boxed InterruptedException", t)) - case Left(e: Error) => throw e + case Left(e: Error) => Left(new ExecutionException("Boxed Error", e)) case _ => source } private val resolverFunction: PartialFunction[Throwable, Either[Throwable, _]] = { case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value) + case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) - case e: Error => throw e + case e: Error => Left(new ExecutionException("Boxed Error", e)) case t => Left(t) } @@ -83,9 +85,12 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def await[T](atMost: Duration)(body: =>T): T = result(new Awaitable[T] { - def await(timeout: Timeout)(implicit cb: CanAwait) = body - }, atMost) + def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + + /** Wraps a block of code into an awaitable object. */ + def body2awaitable[T](body: =>T) = new Awaitable[T] { + def await(atMost: Duration)(implicit cb: CanAwait) = body + } /** Blocks on a blockable object. * @@ -96,18 +101,23 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def result[T](awaitable: Awaitable[T], atMost: Duration): T = { + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { currentExecutionContext.get match { case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case - case x => x.blockingCall(atMost, awaitable) // inside an execution context thread + case x => x.blockingCall(awaitable) // inside an execution context thread } } - def ready[T](awaitable: Awaitable[T], atMost: Duration): Awaitable[T] = { - result(awaitable, atMost) - awaitable + object await { + def ready[T](awaitable: Awaitable[T], atMost: Duration): Awaitable[T] = { + try blocking(awaitable, atMost) + catch { case _ => } + awaitable + } + + def result[T](awaitable: Awaitable[T], atMost: Duration): T = blocking(awaitable, atMost) } - + } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index abd363cedf..a951c09da2 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -10,9 +10,7 @@ import scala.concurrent.{ } import scala.concurrent.future import scala.concurrent.promise -import scala.concurrent.await -import scala.concurrent.result -import scala.concurrent.ready +import scala.concurrent.blocking import scala.util.Duration @@ -314,7 +312,7 @@ trait FutureProjections extends TestBase { val f = future { throw cause } - assert(result(f.failed, Duration(500, "ms")) == cause) + assert(blocking(f.failed, Duration(500, "ms")) == cause) done() } @@ -322,7 +320,7 @@ trait FutureProjections extends TestBase { done => val f = future { 0 } try { - ready(f.failed, Duration(0, "ms")) + blocking(f.failed, Duration(0, "ms")) assert(false) } catch { case nsee: NoSuchElementException => done() @@ -344,7 +342,7 @@ trait Blocking extends TestBase { def testAwaitSuccess(): Unit = once { done => val f = future { 0 } - ready(f, Duration(500, "ms")) + blocking(f, Duration(500, "ms")) done() } @@ -355,7 +353,7 @@ trait Blocking extends TestBase { throw cause } try { - ready(f, Duration(500, "ms")) + blocking(f, Duration(500, "ms")) assert(false) } catch { case t => -- cgit v1.2.3 From 8b5f05ac364dd13f6b0443690825adc382ff8fc7 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 16 Jan 2012 13:44:05 +0100 Subject: Add execution context implementation to akka futures. --- .../scala/concurrent/ExecutionContext.scala | 8 ++ .../concurrent/akka/ExecutionContextImpl.scala | 102 +++++++++++++++++++++ src/library/scala/concurrent/akka/Promise.scala | 85 ++++++++++------- 3 files changed, 163 insertions(+), 32 deletions(-) create mode 100644 src/library/scala/concurrent/akka/ExecutionContextImpl.scala (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 38a28044e1..078b05c517 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -1,3 +1,11 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + package scala.concurrent diff --git a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala new file mode 100644 index 0000000000..28638d1247 --- /dev/null +++ b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala @@ -0,0 +1,102 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent.akka + + + +import java.util.concurrent.{Callable, ExecutorService} +import scala.concurrent.{ExecutionContext, resolver, Awaitable} +import scala.util.Duration +import scala.collection.mutable.Stack + + + +class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionContext { + + def execute(runnable: Runnable): Unit = executorService execute runnable + + def execute[U](body: () => U): Unit = execute(new Runnable { + def run() = body() + }) + + def promise[T]: Promise[T] = new Promise.DefaultPromise[T]()(this) + + def future[T](body: =>T): Future[T] = { + val p = promise[T] + + dispatchFuture { + () => + p complete { + try { + Right(body) + } catch { + case e => resolver(e) + } + } + } + + p.future + } + + /** Only callable from the tasks running on the same execution context. */ + def blockingCall[T](body: Awaitable[T]): T = { + releaseStack() + + // TODO see what to do with timeout + body.await(Duration.fromNanos(0))(CanAwaitEvidence) + } + + // an optimization for batching futures + // TODO we should replace this with a public queue, + // so that it can be stolen from + // OR: a push to the local task queue should be so cheap that this is + // not even needed, but stealing is still possible + private val _taskStack = new ThreadLocal[Stack[() => Unit]]() + + private def releaseStack(): Unit = + _taskStack.get match { + case stack if (stack ne null) && stack.nonEmpty => + val tasks = stack.elems + stack.clear() + _taskStack.remove() + dispatchFuture(() => _taskStack.get.elems = tasks, true) + case null => + // do nothing - there is no local batching stack anymore + case _ => + _taskStack.remove() + } + + private[akka] def dispatchFuture(task: () => Unit, force: Boolean = false): Unit = + _taskStack.get match { + case stack if (stack ne null) && !force => stack push task + case _ => this.execute( + new Runnable { + def run() { + try { + val taskStack = Stack[() => Unit](task) + _taskStack set taskStack + while (taskStack.nonEmpty) { + val next = taskStack.pop() + try { + next.apply() + } catch { + case e => + // TODO catching all and continue isn't good for OOME + e.printStackTrace() + } + } + } finally { + _taskStack.remove() + } + } + } + ) + } + +} diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index d3b93b9573..3b77f14f70 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -12,7 +12,7 @@ package scala.concurrent.akka import java.util.concurrent.TimeUnit.{ NANOSECONDS, MILLISECONDS } import java.util.concurrent.atomic.AtomicReferenceFieldUpdater -import scala.concurrent.{Awaitable, ExecutionContext, resolver, blocking, CanAwait, TimeoutException} +import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} import scala.util.continuations._ import scala.util.Duration import scala.annotation.tailrec @@ -21,6 +21,8 @@ import scala.annotation.tailrec trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { + def future = this + // TODO refine answer and return types here from Any to type parameters // then move this up in the hierarchy @@ -75,7 +77,7 @@ object Promise { */ sealed trait FState[+T] { def value: Option[Either[Throwable, T]] } - case class Pending[T](listeners: List[Either[Throwable, T] ⇒ Unit] = Nil) extends FState[T] { + case class Pending[T](listeners: List[Either[Throwable, T] => Any] = Nil) extends FState[T] { def value: Option[Either[Throwable, T]] = None } @@ -89,8 +91,9 @@ object Promise { private val emptyPendingValue = Pending[Nothing](Nil) - /* default promise implementation */ - abstract class DefaultPromise[T](implicit val executor: ExecutionContext) extends AbstractPromise with Promise[T] { + /** Default promise implementation. + */ + class DefaultPromise[T](implicit val executor: ExecutionContextImpl) extends AbstractPromise with Promise[T] { self => updater.set(this, Promise.EmptyPending()) @@ -102,7 +105,13 @@ object Promise { val ms = NANOSECONDS.toMillis(waitTimeNanos) val ns = (waitTimeNanos % 1000000l).toInt // as per object.wait spec val start = System.nanoTime() - try { synchronized { if (value.isEmpty) wait(ms, ns) } } catch { case e: InterruptedException ⇒ } + try { + synchronized { + while (value.isEmpty) wait(ms, ns) + } + } catch { + case e: InterruptedException => + } awaitUnsafe(waitTimeNanos - (System.nanoTime() - start)) } else @@ -133,80 +142,92 @@ object Promise { @inline protected final def getState: FState[T] = updater.get(this) - /* def tryComplete(value: Either[Throwable, T]): Boolean = { - val callbacks: List[Either[Throwable, T] => Unit] = { + val callbacks: List[Either[Throwable, T] => Any] = { try { @tailrec - def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Unit] = { + def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Any] = { getState match { case cur @ Pending(listeners) => - if (updateState(cur, if (v.isLeft) Failure(Some(v)) else Success(Some(v)))) listeners - else tryComplete(v) + if (updateState(cur, if (v.isLeft) Failure(Some(v)) else Success(Some(v)))) listeners + else tryComplete(v) case _ => null } } tryComplete(resolve(value)) } finally { - synchronized { notifyAll() } //Notify any evil blockers + synchronized { notifyAll() } // notify any blockers from `tryAwait` } } callbacks match { case null => false case cs if cs.isEmpty => true - case cs => Future.dispatchTask(() => cs.foreach(f => notifyCompleted(f, value))); true + case cs => + executor dispatchFuture { + () => cs.foreach(f => notifyCompleted(f, value)) + } + true } } - def onComplete(func: Either[Throwable, T] => Unit): this.type = { - @tailrec //Returns whether the future has already been completed or not + def onComplete[U](func: Either[Throwable, T] => U): this.type = { + @tailrec // Returns whether the future has already been completed or not def tryAddCallback(): Boolean = { val cur = getState cur match { case _: Success[_] | _: Failure[_] => true case p: Pending[_] => - val pt = p.asInstanceOf[Pending[T]] - if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false else tryAddCallback() + val pt = p.asInstanceOf[Pending[T]] + if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false else tryAddCallback() } } if (tryAddCallback()) { val result = value.get - Future.dispatchTask(() => notifyCompleted(func, result)) + executor dispatchFuture { + () => notifyCompleted(func, result) + } } this } - private final def notifyCompleted(func: Either[Throwable, T] => Unit, result: Either[Throwable, T]) { - try { func(result) } catch { case e => logError("Future onComplete-callback raised an exception", e) } + private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T]) { + // TODO see what to do about logging + //try { + func(result) + //} catch { + // case e => logError("Future onComplete-callback raised an exception", e) + //} } - */ } - /* - /** - * An already completed Future is seeded with it's result at creation, is useful for when you are participating in - * a Future-composition but you already have a value to contribute. + /** An already completed Future is given its result at creation. + * + * Useful in Future-composition when a value to contribute is already available. */ - final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContext) extends Promise[T] { + final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { val value = Some(resolve(suppliedValue)) - + def tryComplete(value: Either[Throwable, T]): Boolean = false - def onComplete(func: Either[Throwable, T] => Unit): this.type = { + + def onComplete[U](func: Either[Throwable, T] => U): this.type = { val completedAs = value.get - Future dispatchTask (() => func(completedAs)) + executor dispatchFuture { + () => func(completedAs) + } this } - - def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this - def result(atMost: Duration)(implicit permit: CanAwait): T = value.get match { + + private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this + + def await(atMost: Duration)(implicit permit: CanAwait): T = value.get match { case Left(e) => throw e case Right(r) => r } } - */ + } -- cgit v1.2.3 From 031eea9cb2b7ff00f70f9adb8d8da371bd013bfe Mon Sep 17 00:00:00 2001 From: brijest Date: Mon, 16 Jan 2012 16:16:08 +0100 Subject: Work in progress. --- .../scala/concurrent/ExecutionContext.scala | 5 ++-- .../concurrent/akka/ExecutionContextImpl.scala | 11 +++++++- src/library/scala/concurrent/akka/Future.scala | 4 +-- src/library/scala/concurrent/akka/Promise.scala | 6 ++--- .../scala/concurrent/default/TaskImpl.scala | 11 +++++++- src/library/scala/concurrent/package.scala | 30 ++++++++++++++-------- 6 files changed, 48 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 078b05c517..2650022e1e 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -30,8 +30,9 @@ trait ExecutionContext { def future[T](body: => T): Future[T] - /** Only callable from the tasks running on the same execution context. */ - def blockingCall[T](body: Awaitable[T]): T + def blocking[T](atMost: Duration)(body: =>T): T + + def blocking[T](atMost: Duration)(awaitable: Awaitable[T]): T } diff --git a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala index 28638d1247..1cff7211f3 100644 --- a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala @@ -44,8 +44,17 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo p.future } + def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + + def blocking[T](atMost: Duration)(awaitable: Awaitable[T]): T = { + currentExecutionContext.get match { + case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case + case x => x.blockingCall(awaitable) // inside an execution context thread + } + } + /** Only callable from the tasks running on the same execution context. */ - def blockingCall[T](body: Awaitable[T]): T = { + private def blockingCall[T](body: Awaitable[T]): T = { releaseStack() // TODO see what to do with timeout diff --git a/src/library/scala/concurrent/akka/Future.scala b/src/library/scala/concurrent/akka/Future.scala index c48009554c..5036768d36 100644 --- a/src/library/scala/concurrent/akka/Future.scala +++ b/src/library/scala/concurrent/akka/Future.scala @@ -11,7 +11,7 @@ package scala.concurrent.akka import scala.concurrent.{Awaitable, ExecutionContext} -import scala.util.continuations._ +//import scala.util.continuations._ @@ -24,7 +24,7 @@ trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { * Returns the result of this Future without blocking, by suspending execution and storing it as a * continuation until the result is available. */ - def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T => Future[Any])) + //def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T => Future[Any])) /** Tests whether this Future has been completed. */ diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index 3b77f14f70..e7b6b50aeb 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -13,7 +13,7 @@ package scala.concurrent.akka import java.util.concurrent.TimeUnit.{ NANOSECONDS, MILLISECONDS } import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} -import scala.util.continuations._ +//import scala.util.continuations._ import scala.util.Duration import scala.annotation.tailrec @@ -25,7 +25,7 @@ trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { // TODO refine answer and return types here from Any to type parameters // then move this up in the hierarchy - + /* final def <<(value: T): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => cont(complete(Right(value))) @@ -47,7 +47,7 @@ trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { p.future } - + */ // TODO finish this once we introduce something like dataflow streams /* diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index a38541df5d..4bddb740ef 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -257,7 +257,16 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { def promise[T]: Promise[T] = new PromiseImpl[T](this) - def blockingCall[T](b: Awaitable[T]): T = b match { + def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + + def blocking[T](atMost: Duration)(awaitable: Awaitable[T]): T = { + currentExecutionContext.get match { + case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case + case x => x.blockingCall(awaitable) // inside an execution context thread + } + } + + private def blockingCall[T](b: Awaitable[T]): T = b match { case fj: TaskImpl[_] if fj.executor.pool eq pool => fj.await(Duration.fromNanos(0)) case _ => diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 7552100af2..d40eb4e2a1 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -76,6 +76,11 @@ package object concurrent { def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] = execCtx promise + /** Wraps a block of code into an awaitable object. */ + def body2awaitable[T](body: =>T) = new Awaitable[T] { + def await(atMost: Duration)(implicit cb: CanAwait) = body + } + /** Used to block on a piece of code which potentially blocks. * * @param body A piece of code which contains potentially blocking or long running calls. @@ -85,14 +90,10 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + def blocking[T](atMost: Duration)(body: =>T)(implicit execCtx: ExecutionContext = executionContext): T = + executionContext.blocking(atMost)(body) - /** Wraps a block of code into an awaitable object. */ - def body2awaitable[T](body: =>T) = new Awaitable[T] { - def await(atMost: Duration)(implicit cb: CanAwait) = body - } - - /** Blocks on a blockable object. + /** Blocks on an awaitable object. * * @param awaitable An object with a `block` method which runs potentially blocking or long running calls. * @@ -101,23 +102,32 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ + def blocking[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): T = + executionContext.blocking(atMost)(awaitable) + + /* + def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { currentExecutionContext.get match { case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case case x => x.blockingCall(awaitable) // inside an execution context thread } } + */ object await { - def ready[T](awaitable: Awaitable[T], atMost: Duration): Awaitable[T] = { + def ready[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): Awaitable[T] = { try blocking(awaitable, atMost) catch { case _ => } awaitable } - def result[T](awaitable: Awaitable[T], atMost: Duration): T = blocking(awaitable, atMost) + def result[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): T = { + blocking(awaitable, atMost) + } } - + } -- cgit v1.2.3 From 51a930f8595049babf5cf625e5f010c60bedc53b Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 16 Jan 2012 19:03:18 +0100 Subject: Refactor concurrent package and execution contexts. --- .../scala/concurrent/ExecutionContext.scala | 37 +++++++++++++++++- src/library/scala/concurrent/Future.scala | 45 ++++++++-------------- .../concurrent/akka/ExecutionContextImpl.scala | 24 ++++++++++-- src/library/scala/concurrent/akka/Future.scala | 2 +- src/library/scala/concurrent/akka/Promise.scala | 2 +- .../scala/concurrent/default/TaskImpl.scala | 23 ++++++++++- src/library/scala/concurrent/package.scala | 23 ++--------- 7 files changed, 101 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 2650022e1e..303489297f 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -13,6 +13,7 @@ package scala.concurrent import java.util.concurrent.{ Executors, Future => JFuture, Callable } import scala.util.{ Duration, Timeout } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } +import scala.collection.generic.CanBuildFrom @@ -32,7 +33,9 @@ trait ExecutionContext { def blocking[T](atMost: Duration)(body: =>T): T - def blocking[T](atMost: Duration)(awaitable: Awaitable[T]): T + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T + + def futureUtilities: FutureUtilities = FutureUtilitiesImpl } @@ -40,3 +43,35 @@ trait ExecutionContext { sealed trait CanAwait +trait FutureUtilities { + + def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { + val builder = cbf(futures) + val p: Promise[Coll[T]] = promise[Coll[T]] + + if (futures.size == 1) futures.head onComplete { + case Left(t) => p failure t + case Right(v) => builder += v + p success builder.result + } else { + val restFutures = all(futures.tail) + futures.head onComplete { + case Left(t) => p failure t + case Right(v) => builder += v + restFutures onComplete { + case Left(t) => p failure t + case Right(vs) => for (v <- vs) builder += v + p success builder.result + } + } + } + + p.future + } + +} + + +object FutureUtilitiesImpl extends FutureUtilities { +} + diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index e6edaea87a..6b358e1e09 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -365,11 +365,11 @@ self => * {{{ * val f = future { sys.error("failed") } * val g = future { 5 } - * val h = f or g + * val h = f any g * await(0) h // evaluates to either 5 or throws a runtime exception * }}} */ - def or[U >: T](that: Future[U]): Future[U] = { + def any[U >: T](that: Future[U]): Future[U] = { val p = newPromise[U] val completePromise: PartialFunction[Either[Throwable, T], _] = { @@ -385,35 +385,24 @@ self => } +/** TODO some docs + * + * @define nonDeterministic + * Note: using this method yields nondeterministic dataflow programs. + */ object Future { - /* - // TODO make more modular by encoding this within the execution context - def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { - val builder = cbf(futures) - val p: Promise[Coll[T]] = executor.promise[Coll[T]] - - if (futures.size == 1) futures.head onComplete { - case Left(t) => p failure t - case Right(v) => builder += v - p success builder.result - } else { - val restFutures = all(futures.tail) - futures.head onComplete { - case Left(t) => p failure t - case Right(v) => builder += v - restFutures onComplete { - case Left(t) => p failure t - case Right(vs) => for (v <- vs) builder += v - p success builder.result - } - } - } - - p.future - } - */ + // TODO make more modular by encoding all other helper methods within the execution context + /** + */ + def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]], ec: ExecutionContext): Future[Coll[T]] = + ec.futureUtilities.all[T, Coll](futures) + // move this to future companion object @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) } + + + + diff --git a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala index 1cff7211f3..922d77189c 100644 --- a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala @@ -11,15 +11,22 @@ package scala.concurrent.akka import java.util.concurrent.{Callable, ExecutorService} -import scala.concurrent.{ExecutionContext, resolver, Awaitable} +import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} import scala.util.Duration import scala.collection.mutable.Stack class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionContext { + import ExecutionContextImpl._ - def execute(runnable: Runnable): Unit = executorService execute runnable + def execute(runnable: Runnable): Unit = executorService match { + // case fj: ForkJoinPool => + // // TODO fork if more applicable + // executorService execute runnable + case _ => + executorService execute runnable + } def execute[U](body: () => U): Unit = execute(new Runnable { def run() = body() @@ -46,7 +53,7 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) - def blocking[T](atMost: Duration)(awaitable: Awaitable[T]): T = { + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { currentExecutionContext.get match { case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case case x => x.blockingCall(awaitable) // inside an execution context thread @@ -109,3 +116,14 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo } } + + +object ExecutionContextImpl { + + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContextImpl] = new ThreadLocal[ExecutionContextImpl] { + override protected def initialValue = null + } + +} + + diff --git a/src/library/scala/concurrent/akka/Future.scala b/src/library/scala/concurrent/akka/Future.scala index 5036768d36..be1a9ec2ae 100644 --- a/src/library/scala/concurrent/akka/Future.scala +++ b/src/library/scala/concurrent/akka/Future.scala @@ -17,7 +17,7 @@ import scala.concurrent.{Awaitable, ExecutionContext} trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { - implicit def executor: ExecutionContext + implicit def executor: ExecutionContextImpl /** For use only within a Future.flow block or another compatible Delimited Continuations reset block. * diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index e7b6b50aeb..d688d3d850 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -118,7 +118,7 @@ object Promise { value.isDefined } - executor.blockingCall(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost)))) + executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), Duration.fromNanos(0)) } private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 4bddb740ef..59037cc48b 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -215,6 +215,8 @@ case class Failure[T](throwable: Throwable) extends State[T] private[concurrent] final class ExecutionContextImpl extends ExecutionContext { + import ExecutionContextImpl._ + val pool = { val p = new ForkJoinPool p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler { @@ -259,10 +261,11 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) - def blocking[T](atMost: Duration)(awaitable: Awaitable[T]): T = { + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { currentExecutionContext.get match { case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case - case x => x.blockingCall(awaitable) // inside an execution context thread + case x if x eq this => this.blockingCall(awaitable) // inside an execution context thread on this executor + case x => x.blocking(awaitable, atMost) } } @@ -286,3 +289,19 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { } } + + +object ExecutionContextImpl { + + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { + override protected def initialValue = null + } + +} + + + + + + + diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index d40eb4e2a1..23f26dd3b5 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -27,17 +27,13 @@ package object concurrent { /** A global execution environment for executing lightweight tasks. */ lazy val executionContext = - new default.ExecutionContextImpl + new akka.ExecutionContextImpl(java.util.concurrent.Executors.newCachedThreadPool()) /** A global service for scheduling tasks for execution. */ lazy val scheduler = new default.SchedulerImpl - private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { - override protected def initialValue = null - } - val handledFutureException: PartialFunction[Throwable, Throwable] = { case t: Throwable if isFutureThrowable(t) => t } @@ -90,7 +86,7 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def blocking[T](atMost: Duration)(body: =>T)(implicit execCtx: ExecutionContext = executionContext): T = + def blocking[T](atMost: Duration)(body: =>T)(implicit execCtx: ExecutionContext): T = executionContext.blocking(atMost)(body) /** Blocks on an awaitable object. @@ -102,19 +98,8 @@ package object concurrent { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def blocking[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): T = - executionContext.blocking(atMost)(awaitable) - - /* - def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { - currentExecutionContext.get match { - case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case - case x => x.blockingCall(awaitable) // inside an execution context thread - } - } - */ + def blocking[T](awaitable: Awaitable[T], atMost: Duration)(implicit execCtx: ExecutionContext = executionContext): T = + executionContext.blocking(awaitable, atMost) object await { def ready[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): Awaitable[T] = { -- cgit v1.2.3 From 62bfdb1c8d4d508b976c2ab6ffdae98e35bd4b76 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 19 Jan 2012 09:26:40 +0100 Subject: Added implementations for any and find on collections of futures. --- .../scala/concurrent/ExecutionContext.scala | 42 ++++++++++++++++++++-- src/library/scala/concurrent/Future.scala | 15 +++++--- src/library/scala/concurrent/Promise.scala | 5 ++- 3 files changed, 55 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 303489297f..5539b6858f 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -11,6 +11,7 @@ package scala.concurrent import java.util.concurrent.{ Executors, Future => JFuture, Callable } +import java.util.concurrent.atomic.{ AtomicInteger } import scala.util.{ Duration, Timeout } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } import scala.collection.generic.CanBuildFrom @@ -45,6 +46,9 @@ sealed trait CanAwait trait FutureUtilities { +/** TODO some docs + * + */ def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { val builder = cbf(futures) val p: Promise[Coll[T]] = promise[Coll[T]] @@ -68,9 +72,43 @@ trait FutureUtilities { p.future } - -} +/** TODO some docs + * + */ + def any[T](futures: Traversable[Future[T]]): Future[T] = { + val futureResult = promise[T]() + + val completeFirst: Either[Throwable, T] => Unit = futureElem => futureResult tryComplete futureElem + + futures.foreach(_ onComplete completeFirst) + + futureResult.future + } + +/** TODO some docs + * + */ + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { + if (futures.isEmpty) Promise.successful[Option[T]](None).future + else { + val result = promise[Option[T]]() + val ref = new AtomicInteger(futures.size) + val search: Either[Throwable, T] ⇒ Unit = { + v ⇒ v match { + case Right(r) ⇒ if (predicate(r)) result trySuccess Some(r) + case _ ⇒ + } + if (ref.decrementAndGet == 0) result trySuccess None + } + + futures.foreach(_ onComplete search) + + result.future + } + } + +} object FutureUtilitiesImpl extends FutureUtilities { } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 6b358e1e09..468683dcde 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -355,7 +355,8 @@ self => p.future } - + +/* /** Creates a new future which holds the result of either this future or `that` future, depending on * which future was completed first. * @@ -377,14 +378,16 @@ self => case Right(v) => p trySuccess v } this onComplete completePromise - this onComplete completePromise + that onComplete completePromise p.future } - + +*/ } + /** TODO some docs * * @define nonDeterministic @@ -393,13 +396,17 @@ self => object Future { // TODO make more modular by encoding all other helper methods within the execution context - /** + /** TODO some docs */ def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]], ec: ExecutionContext): Future[Coll[T]] = ec.futureUtilities.all[T, Coll](futures) // move this to future companion object @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) + + def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[T] = ec.futureUtilities.any(futures) + + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext): Future[Option[T]] = ec.futureUtilities.find(futures)(predicate) } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index c3fa92053b..41a41dd611 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -113,6 +113,9 @@ trait Promise[T] { object Promise { - + def successful[T](result: T): Promise[T] = { + val p = promise[T]() + p.success(result) + } } -- cgit v1.2.3 From 09deeec60db0e6e6b6904041db43535e492a0c2d Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 19 Jan 2012 17:41:11 +0100 Subject: Fix `all` combinator on futures, refactor execution context, remove disabled files. --- .../scala/concurrent/AbstractPromise.java.disabled | 17 - .../scala/concurrent/ExecutionContext.scala | 76 +- src/library/scala/concurrent/Future.scala | 17 +- src/library/scala/concurrent/Future.scala.disabled | 1051 -------------------- src/library/scala/concurrent/Promise.scala | 1 - src/library/scala/concurrent/package.scala | 80 +- .../scala/concurrent/package.scala.disabled | 108 -- 7 files changed, 88 insertions(+), 1262 deletions(-) delete mode 100644 src/library/scala/concurrent/AbstractPromise.java.disabled delete mode 100644 src/library/scala/concurrent/Future.scala.disabled delete mode 100644 src/library/scala/concurrent/package.scala.disabled (limited to 'src') diff --git a/src/library/scala/concurrent/AbstractPromise.java.disabled b/src/library/scala/concurrent/AbstractPromise.java.disabled deleted file mode 100644 index 726e6a3156..0000000000 --- a/src/library/scala/concurrent/AbstractPromise.java.disabled +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2011 Typesafe Inc. - */ - -package scala.concurrent; -/* -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import scala.concurrent.forkjoin.*; - -abstract class AbstractPromise { - //private volatile Object _ref = DefaultPromise.EmptyPending(); - protected final static AtomicReferenceFieldUpdater updater = - AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); - - protected void compute() { } -} -*/ diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 5539b6858f..ebd5bf6bd3 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -11,14 +11,12 @@ package scala.concurrent import java.util.concurrent.{ Executors, Future => JFuture, Callable } -import java.util.concurrent.atomic.{ AtomicInteger } -import scala.util.{ Duration, Timeout } +import scala.util.Duration import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } -import scala.collection.generic.CanBuildFrom -trait ExecutionContext { +trait ExecutionContext extends ExecutionContextBase { protected implicit object CanAwaitEvidence extends CanAwait @@ -36,80 +34,10 @@ trait ExecutionContext { def blocking[T](awaitable: Awaitable[T], atMost: Duration): T - def futureUtilities: FutureUtilities = FutureUtilitiesImpl - } sealed trait CanAwait -trait FutureUtilities { - -/** TODO some docs - * - */ - def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { - val builder = cbf(futures) - val p: Promise[Coll[T]] = promise[Coll[T]] - - if (futures.size == 1) futures.head onComplete { - case Left(t) => p failure t - case Right(v) => builder += v - p success builder.result - } else { - val restFutures = all(futures.tail) - futures.head onComplete { - case Left(t) => p failure t - case Right(v) => builder += v - restFutures onComplete { - case Left(t) => p failure t - case Right(vs) => for (v <- vs) builder += v - p success builder.result - } - } - } - - p.future - } - -/** TODO some docs - * - */ - def any[T](futures: Traversable[Future[T]]): Future[T] = { - val futureResult = promise[T]() - - val completeFirst: Either[Throwable, T] => Unit = futureElem => futureResult tryComplete futureElem - - futures.foreach(_ onComplete completeFirst) - - futureResult.future - } - -/** TODO some docs - * - */ - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { - if (futures.isEmpty) Promise.successful[Option[T]](None).future - else { - val result = promise[Option[T]]() - val ref = new AtomicInteger(futures.size) - val search: Either[Throwable, T] ⇒ Unit = { - v ⇒ v match { - case Right(r) ⇒ if (predicate(r)) result trySuccess Some(r) - case _ ⇒ - } - if (ref.decrementAndGet == 0) result trySuccess None - } - - futures.foreach(_ onComplete search) - - result.future - } - } - -} - -object FutureUtilitiesImpl extends FutureUtilities { -} diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 468683dcde..ff7da8433a 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -355,8 +355,7 @@ self => p.future } - -/* + /** Creates a new future which holds the result of either this future or `that` future, depending on * which future was completed first. * @@ -370,20 +369,20 @@ self => * await(0) h // evaluates to either 5 or throws a runtime exception * }}} */ - def any[U >: T](that: Future[U]): Future[U] = { + def either[U >: T](that: Future[U]): Future[U] = { val p = newPromise[U] - val completePromise: PartialFunction[Either[Throwable, T], _] = { + val completePromise: PartialFunction[Either[Throwable, U], _] = { case Left(t) => p tryFailure t case Right(v) => p trySuccess v } + this onComplete completePromise that onComplete completePromise p.future } - -*/ + } @@ -399,14 +398,14 @@ object Future { /** TODO some docs */ def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]], ec: ExecutionContext): Future[Coll[T]] = - ec.futureUtilities.all[T, Coll](futures) + ec.all[T, Coll](futures) // move this to future companion object @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) - def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[T] = ec.futureUtilities.any(futures) + def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[T] = ec.any(futures) - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext): Future[Option[T]] = ec.futureUtilities.find(futures)(predicate) + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext): Future[Option[T]] = ec.find(futures)(predicate) } diff --git a/src/library/scala/concurrent/Future.scala.disabled b/src/library/scala/concurrent/Future.scala.disabled deleted file mode 100644 index 3cd9bbeb6e..0000000000 --- a/src/library/scala/concurrent/Future.scala.disabled +++ /dev/null @@ -1,1051 +0,0 @@ -/* -class FutureFactory(dispatcher: MessageDispatcher, timeout: Timeout) { - - // TODO: remove me ASAP !!! - implicit val _dispatcher = dispatcher - - /** - * Java API, equivalent to Future.apply - */ - def future[T](body: Callable[T]): Future[T] = - Future(body.call, timeout) - - /** - * Java API, equivalent to Future.apply - */ - def future[T](body: Callable[T], timeout: Timeout): Future[T] = - Future(body.call, timeout) - - /** - * Java API, equivalent to Future.apply - */ - def future[T](body: Callable[T], timeout: Long): Future[T] = - Future(body.call, timeout) - - /** - * Java API, equivalent to Future.apply - */ - def future[T](body: Callable[T], dispatcher: MessageDispatcher): Future[T] = - Future(body.call)(dispatcher, timeout) - - /** - * Java API, equivalent to Future.apply - */ - def future[T](body: Callable[T], timeout: Timeout, dispatcher: MessageDispatcher): Future[T] = - Future(body.call)(dispatcher, timeout) - - /** - * Java API, equivalent to Future.apply - */ - def future[T](body: Callable[T], timeout: Long, dispatcher: MessageDispatcher): Future[T] = - Future(body.call)(dispatcher, timeout) - - /** - * Java API. - * Returns a Future that will hold the optional result of the first Future with a result that matches the predicate - */ -/* - def find[T <: AnyRef](futures: JIterable[Future[T]], predicate: JFunc[T, java.lang.Boolean], timeout: Timeout): Future[JOption[T]] = { - val pred: T ⇒ Boolean = predicate.apply(_) - Future.find[T]((scala.collection.JavaConversions.iterableAsScalaIterable(futures)), timeout)(pred).map(JOption.fromScalaOption(_))(timeout) - } - - def find[T <: AnyRef](futures: JIterable[Future[T]], predicate: JFunc[T, java.lang.Boolean]): Future[JOption[T]] = find(futures, predicate, timeout) -*/ - /** - * Java API. - * Returns a Future to the result of the first future in the list that is completed - */ -/* - def firstCompletedOf[T <: AnyRef](futures: JIterable[Future[T]], timeout: Timeout): Future[T] = - Future.firstCompletedOf(scala.collection.JavaConversions.iterableAsScalaIterable(futures), timeout) - - def firstCompletedOf[T <: AnyRef](futures: JIterable[Future[T]]): Future[T] = firstCompletedOf(futures, timeout) -*/ - /** - * Java API - * A non-blocking fold over the specified futures. - * The fold is performed on the thread where the last future is completed, - * the result will be the first failure of any of the futures, or any failure in the actual fold, - * or the result of the fold. - */ -/* - def fold[T <: AnyRef, R <: AnyRef](zero: R, timeout: Timeout, futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, R]): Future[R] = - Future.fold(scala.collection.JavaConversions.iterableAsScalaIterable(futures), timeout)(zero)(fun.apply _) - - def fold[T <: AnyRef, R <: AnyRef](zero: R, timeout: Long, futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, R]): Future[R] = fold(zero, timeout: Timeout, futures, fun) - - def fold[T <: AnyRef, R <: AnyRef](zero: R, futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, R]): Future[R] = fold(zero, timeout, futures, fun) - - /** - * Java API. - * Initiates a fold over the supplied futures where the fold-zero is the result value of the Future that's completed first - */ - def reduce[T <: AnyRef, R >: T](futures: java.lang.Iterable[Future[T]], timeout: Timeout, fun: akka.japi.Function2[R, T, T]): Future[R] = - Future.reduce(scala.collection.JavaConversions.iterableAsScalaIterable(futures), timeout)(fun.apply _) - - def reduce[T <: AnyRef, R >: T](futures: java.lang.Iterable[Future[T]], timeout: Long, fun: akka.japi.Function2[R, T, T]): Future[R] = reduce(futures, timeout: Timeout, fun) - - def reduce[T <: AnyRef, R >: T](futures: java.lang.Iterable[Future[T]], fun: akka.japi.Function2[R, T, T]): Future[R] = reduce(futures, timeout, fun) - - /** - * Java API. - * Simple version of Future.traverse. Transforms a java.lang.Iterable[Future[A]] into a Future[java.lang.Iterable[A]]. - * Useful for reducing many Futures into a single Future. - */ - def sequence[A](in: JIterable[Future[A]], timeout: Timeout): Future[JIterable[A]] = { - implicit val t = timeout - scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[A]()))((fr, fa) ⇒ - for (r ← fr; a ← fa) yield { - r add a - r - }) - } - - def sequence[A](in: JIterable[Future[A]]): Future[JIterable[A]] = sequence(in, timeout) - - /** - * Java API. - * Transforms a java.lang.Iterable[A] into a Future[java.lang.Iterable[B]] using the provided Function A ⇒ Future[B]. - * This is useful for performing a parallel map. For example, to apply a function to all items of a list - * in parallel. - */ - def traverse[A, B](in: JIterable[A], timeout: Timeout, fn: JFunc[A, Future[B]]): Future[JIterable[B]] = { - implicit val t = timeout - scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[B]())) { (fr, a) ⇒ - val fb = fn(a) - for (r ← fr; b ← fb) yield { - r add b - r - } - } - } - - def traverse[A, B](in: JIterable[A], fn: JFunc[A, Future[B]]): Future[JIterable[B]] = traverse(in, timeout, fn) - -*/ -} -*/ - -object Future { - /* - /** - * This method constructs and returns a Future that will eventually hold the result of the execution of the supplied body - * The execution is performed by the specified Dispatcher. - */ - def apply[T](body: ⇒ T)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[T] = { - val promise: Promise[T] = DefaultPromise[T](timeout) - - dispatcher dispatchTask { () ⇒ - promise complete { - try { - Right(body) - } catch { - case e ⇒ Left(e) - } - } - } - - promise - } - - def apply[T](body: ⇒ T, timeout: Timeout)(implicit dispatcher: MessageDispatcher): Future[T] = - apply(body)(dispatcher, timeout) - - def apply[T](body: ⇒ T, timeout: Duration)(implicit dispatcher: MessageDispatcher): Future[T] = - apply(body)(dispatcher, timeout) - - def apply[T](body: ⇒ T, timeout: Long)(implicit dispatcher: MessageDispatcher): Future[T] = - apply(body)(dispatcher, timeout) - - import scala.collection.mutable.Builder - import scala.collection.generic.CanBuildFrom - - /** - * Simple version of Futures.traverse. Transforms a Traversable[Future[A]] into a Future[Traversable[A]]. - * Useful for reducing many Futures into a single Future. - */ - def sequence[A, M[_] <: Traversable[_]](in: M[Future[A]])(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], timeout: Timeout, dispatcher: MessageDispatcher): Future[M[A]] = - in.foldLeft(new KeptPromise(Right(cbf(in))): Future[Builder[A, M[A]]])((fr, fa) ⇒ for (r ← fr; a ← fa.asInstanceOf[Future[A]]) yield (r += a)).map(_.result) - - def sequence[A, M[_] <: Traversable[_]](in: M[Future[A]], timeout: Timeout)(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], dispatcher: MessageDispatcher): Future[M[A]] = - sequence(in)(cbf, timeout, dispatcher) - - /** - * Returns a Future to the result of the first future in the list that is completed - */ - def firstCompletedOf[T](futures: Iterable[Future[T]])(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[T] = { - val futureResult = DefaultPromise[T](timeout) - - val completeFirst: Future[T] ⇒ Unit = _.value.foreach(futureResult complete _) - futures.foreach(_ onComplete completeFirst) - - futureResult - } - - def firstCompletedOf[T](futures: Iterable[Future[T]], timeout: Timeout)(implicit dispatcher: MessageDispatcher): Future[T] = - firstCompletedOf(futures)(dispatcher, timeout) - - /** - * Returns a Future that will hold the optional result of the first Future with a result that matches the predicate - */ - def find[T](futures: Iterable[Future[T]])(predicate: T ⇒ Boolean)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[Option[T]] = { - if (futures.isEmpty) new KeptPromise[Option[T]](Right(None)) - else { - val result = DefaultPromise[Option[T]](timeout) - val ref = new AtomicInteger(futures.size) - val search: Future[T] ⇒ Unit = f ⇒ try { - f.result.filter(predicate).foreach(r ⇒ result completeWithResult Some(r)) - } finally { - if (ref.decrementAndGet == 0) - result completeWithResult None - } - futures.foreach(_ onComplete search) - - result - } - } - - def find[T](futures: Iterable[Future[T]], timeout: Timeout)(predicate: T ⇒ Boolean)(implicit dispatcher: MessageDispatcher): Future[Option[T]] = - find(futures)(predicate)(dispatcher, timeout) - - /** - * A non-blocking fold over the specified futures. - * The fold is performed on the thread where the last future is completed, - * the result will be the first failure of any of the futures, or any failure in the actual fold, - * or the result of the fold. - * Example: - *
-   *   val result = Futures.fold(0)(futures)(_ + _).await.result
-   * 
- */ -/* - def fold[T, R](futures: Iterable[Future[T]])(zero: R)(foldFun: (R, T) ⇒ R)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[R] = { - if (futures.isEmpty) { - new KeptPromise[R](Right(zero)) - } else { - val result = DefaultPromise[R](timeout) - val results = new ConcurrentLinkedQueue[T]() - val done = new Switch(false) - val allDone = futures.size - - val aggregate: Future[T] ⇒ Unit = f ⇒ if (done.isOff && !result.isCompleted) { //TODO: This is an optimization, is it premature? - f.value.get match { - case Right(value) ⇒ - val added = results add value - if (added && results.size == allDone) { //Only one thread can get here - if (done.switchOn) { - try { - val i = results.iterator - var currentValue = zero - while (i.hasNext) { currentValue = foldFun(currentValue, i.next) } - result completeWithResult currentValue - } catch { - case e: Exception ⇒ - //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.fold", e.getMessage)) - result completeWithException e - } finally { - results.clear - } - } - } - case Left(exception) ⇒ - if (done.switchOn) { - result completeWithException exception - results.clear - } - } - } - - futures foreach { _ onComplete aggregate } - result - } - } -*/ -/* - def fold[T, R](futures: Iterable[Future[T]], timeout: Timeout)(zero: R)(foldFun: (R, T) ⇒ R)(implicit dispatcher: MessageDispatcher): Future[R] = - fold(futures)(zero)(foldFun)(dispatcher, timeout) -*/ - /** - * Initiates a fold over the supplied futures where the fold-zero is the result value of the Future that's completed first - * Example: - *
-   *   val result = Futures.reduce(futures)(_ + _).await.result
-   * 
- */ -/* - def reduce[T, R >: T](futures: Iterable[Future[T]])(op: (R, T) ⇒ T)(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[R] = { - if (futures.isEmpty) - new KeptPromise[R](Left(new UnsupportedOperationException("empty reduce left"))) - else { - val result = DefaultPromise[R](timeout) - val seedFound = new AtomicBoolean(false) - val seedFold: Future[T] ⇒ Unit = f ⇒ { - if (seedFound.compareAndSet(false, true)) { //Only the first completed should trigger the fold - f.value.get match { - case Right(value) ⇒ result.completeWith(fold(futures.filterNot(_ eq f))(value)(op)) - case Left(exception) ⇒ result.completeWithException(exception) - } - } - } - for (f ← futures) f onComplete seedFold //Attach the listener to the Futures - result - } - } -*/ -/* - def reduce[T, R >: T](futures: Iterable[Future[T]], timeout: Timeout)(op: (R, T) ⇒ T)(implicit dispatcher: MessageDispatcher): Future[R] = - reduce(futures)(op)(dispatcher, timeout) -*/ - /** - * Transforms a Traversable[A] into a Future[Traversable[B]] using the provided Function A ⇒ Future[B]. - * This is useful for performing a parallel map. For example, to apply a function to all items of a list - * in parallel: - *
-   * val myFutureList = Futures.traverse(myList)(x ⇒ Future(myFunc(x)))
-   * 
- */ - def traverse[A, B, M[_] <: Traversable[_]](in: M[A])(fn: A ⇒ Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], timeout: Timeout, dispatcher: MessageDispatcher): Future[M[B]] = - in.foldLeft(new KeptPromise(Right(cbf(in))): Future[Builder[B, M[B]]]) { (fr, a) ⇒ - val fb = fn(a.asInstanceOf[A]) - for (r ← fr; b ← fb) yield (r += b) - }.map(_.result) - - def traverse[A, B, M[_] <: Traversable[_]](in: M[A], timeout: Timeout)(fn: A ⇒ Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], dispatcher: MessageDispatcher): Future[M[B]] = - traverse(in)(fn)(cbf, timeout, dispatcher) - - /** - * Captures a block that will be transformed into 'Continuation Passing Style' using Scala's Delimited - * Continuations plugin. - * - * Within the block, the result of a Future may be accessed by calling Future.apply. At that point - * execution is suspended with the rest of the block being stored in a continuation until the result - * of the Future is available. If an Exception is thrown while processing, it will be contained - * within the resulting Future. - * - * This allows working with Futures in an imperative style without blocking for each result. - * - * Completing a Future using 'Promise << Future' will also suspend execution until the - * value of the other Future is available. - * - * The Delimited Continuations compiler plugin must be enabled in order to use this method. - */ -/* - def flow[A](body: ⇒ A @cps[Future[Any]])(implicit dispatcher: MessageDispatcher, timeout: Timeout): Future[A] = { - val future = Promise[A](timeout) - dispatchTask({ () ⇒ - (reify(body) foreachFull (future completeWithResult, future completeWithException): Future[Any]) onException { - case e: Exception ⇒ future completeWithException e - } - }, true) - future - } -*/ - // TODO make variant of flow(timeout)(body) which does NOT break type inference - - /** - * Assures that any Future tasks initiated in the current thread will be - * executed asynchronously, including any tasks currently queued to be - * executed in the current thread. This is needed if the current task may - * block, causing delays in executing the remaining tasks which in some - * cases may cause a deadlock. - * - * Note: Calling 'Future.await' will automatically trigger this method. - * - * For example, in the following block of code the call to 'latch.open' - * might not be executed until after the call to 'latch.await', causing - * a deadlock. By adding 'Future.blocking()' the call to 'latch.open' - * will instead be dispatched separately from the current block, allowing - * it to be run in parallel: - *
-   * val latch = new StandardLatch
-   * val future = Future() map { _ ⇒
-   *   Future.blocking()
-   *   val nested = Future()
-   *   nested foreach (_ ⇒ latch.open)
-   *   latch.await
-   * }
-   * 
- */ - def blocking()(implicit dispatcher: MessageDispatcher): Unit = - _taskStack.get match { - case Some(taskStack) if taskStack.nonEmpty ⇒ - val tasks = taskStack.elems - taskStack.clear() - _taskStack set None - dispatchTask(() ⇒ _taskStack.get.get.elems = tasks, true) - case Some(_) ⇒ _taskStack set None - case _ ⇒ // already None - } - - private val _taskStack = new ThreadLocal[Option[Stack[() ⇒ Unit]]]() { - override def initialValue = None - } - - private[concurrent] def dispatchTask(task: () ⇒ Unit, force: Boolean = false)(implicit dispatcher: MessageDispatcher): Unit = - _taskStack.get match { - case Some(taskStack) if !force ⇒ taskStack push task - case _ ⇒ - dispatcher dispatchTask { () ⇒ - try { - val taskStack = Stack[() ⇒ Unit](task) - _taskStack set Some(taskStack) - while (taskStack.nonEmpty) { - val next = taskStack.pop() - try { - next.apply() - } catch { - case e ⇒ e.printStackTrace() //TODO FIXME strategy for handling exceptions in callbacks - } - } - } finally { _taskStack set None } - } - } - */ -} - -//trait Future[+T] { - /* - /** - * For use only within a Future.flow block or another compatible Delimited Continuations reset block. - * - * Returns the result of this Future without blocking, by suspending execution and storing it as a - * continuation until the result is available. - */ - def apply()(implicit timeout: Timeout): T @cps[Future[Any]] = shift(this flatMap (_: T ⇒ Future[Any])) - - /** - * Blocks awaiting completion of this Future, then returns the resulting value, - * or throws the completed exception - * - * Scala & Java API - * - * throws FutureTimeoutException if this Future times out when waiting for completion - */ - def get: T = this.await.resultOrException.get - - /** - * Blocks the current thread until the Future has been completed or the - * timeout has expired. In the case of the timeout expiring a - * FutureTimeoutException will be thrown. - */ - def await: Future[T] - */ - - /** - * Blocks the current thread until the Future has been completed or the - * timeout has expired, additionally bounding the waiting period according to - * the atMost parameter. The timeout will be the lesser value of - * 'atMost' and the timeout supplied at the constructuion of this Future. In - * the case of the timeout expiring a FutureTimeoutException will be thrown. - * Other callers of this method are not affected by the additional bound - * imposed by atMost. - */ -// def await(atMost: Duration): Future[T] - -/* - /** - * Await completion of this Future and return its value if it conforms to A's - * erased type. Will throw a ClassCastException if the value does not - * conform, or any exception the Future was completed with. Will return None - * in case of a timeout. - */ - def as[A](implicit m: Manifest[A]): Option[A] = { - try await catch { case _: FutureTimeoutException ⇒ } - value match { - case None ⇒ None - case Some(Left(ex)) ⇒ throw ex - case Some(Right(v)) ⇒ - try { Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) } catch { - case c: ClassCastException ⇒ - if (v.asInstanceOf[AnyRef] eq null) throw new ClassCastException("null cannot be cast to " + m.erasure) - else throw new ClassCastException("'" + v + "' of class " + v.asInstanceOf[AnyRef].getClass + " cannot be cast to " + m.erasure) - } - } - } - - /** - * Await completion of this Future and return its value if it conforms to A's - * erased type, None otherwise. Will throw any exception the Future was - * completed with. Will return None in case of a timeout. - */ - def asSilently[A](implicit m: Manifest[A]): Option[A] = { - try await catch { case _: FutureTimeoutException ⇒ } - value match { - case None ⇒ None - case Some(Left(ex)) ⇒ throw ex - case Some(Right(v)) ⇒ - try Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) - catch { case _: ClassCastException ⇒ None } - } - } - - /** - * Tests whether this Future has been completed. - */ - final def isCompleted: Boolean = value.isDefined - - /** - * Tests whether this Future's timeout has expired. - * - * Note that an expired Future may still contain a value, or it may be - * completed with a value. - */ - def isExpired: Boolean - - def timeout: Timeout - - /** - * This Future's timeout in nanoseconds. - */ - def timeoutInNanos = if (timeout.duration.isFinite) timeout.duration.toNanos else Long.MaxValue - - /** - * The contained value of this Future. Before this Future is completed - * the value will be None. After completion the value will be Some(Right(t)) - * if it contains a valid result, or Some(Left(error)) if it contains - * an exception. - */ - def value: Option[Either[Throwable, T]] - - /** - * Returns the successful result of this Future if it exists. - */ - final def result: Option[T] = value match { - case Some(Right(r)) ⇒ Some(r) - case _ ⇒ None - } - - /** - * Returns the contained exception of this Future if it exists. - */ - final def exception: Option[Throwable] = value match { - case Some(Left(e)) ⇒ Some(e) - case _ ⇒ None - } - - /** - * When this Future is completed, apply the provided function to the - * Future. If the Future has already been completed, this will apply - * immediately. Will not be called in case of a timeout, which also holds if - * corresponding Promise is attempted to complete after expiry. Multiple - * callbacks may be registered; there is no guarantee that they will be - * executed in a particular order. - */ - def onComplete(func: Future[T] ⇒ Unit): this.type - - /** - * When the future is completed with a valid result, apply the provided - * PartialFunction to the result. See `onComplete` for more details. - *
-   *   future onResult {
-   *     case Foo ⇒ target ! "foo"
-   *     case Bar ⇒ target ! "bar"
-   *   }
-   * 
- */ - final def onResult(pf: PartialFunction[T, Unit]): this.type = onComplete { - _.value match { - case Some(Right(r)) if pf isDefinedAt r ⇒ pf(r) - case _ ⇒ - } - } - - /** - * When the future is completed with an exception, apply the provided - * PartialFunction to the exception. See `onComplete` for more details. - *
-   *   future onException {
-   *     case NumberFormatException ⇒ target ! "wrong format"
-   *   }
-   * 
- */ - final def onException(pf: PartialFunction[Throwable, Unit]): this.type = onComplete { - _.value match { - case Some(Left(ex)) if pf isDefinedAt ex ⇒ pf(ex) - case _ ⇒ - } - } - - def onTimeout(func: Future[T] ⇒ Unit): this.type - - def orElse[A >: T](fallback: ⇒ A): Future[A] - - /** - * Creates a new Future that will handle any matching Throwable that this - * Future might contain. If there is no match, or if this Future contains - * a valid result then the new Future will contain the same. - * Example: - *
-   * Future(6 / 0) recover { case e: ArithmeticException ⇒ 0 } // result: 0
-   * Future(6 / 0) recover { case e: NotFoundException   ⇒ 0 } // result: exception
-   * Future(6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3
-   * 
- */ - final def recover[A >: T](pf: PartialFunction[Throwable, A])(implicit timeout: Timeout): Future[A] = { - val future = DefaultPromise[A](timeout) - onComplete { - _.value.get match { - case Left(e) if pf isDefinedAt e ⇒ future.complete(try { Right(pf(e)) } catch { case x: Exception ⇒ Left(x) }) - case otherwise ⇒ future complete otherwise - } - } - future - } - - /** - * Creates a new Future by applying a function to the successful result of - * this Future. If this Future is completed with an exception then the new - * Future will also contain this exception. - * Example: - *
-   * val future1 = for {
-   *   a: Int    <- actor ? "Hello" // returns 5
-   *   b: String <- actor ? a       // returns "10"
-   *   c: String <- actor ? 7       // returns "14"
-   * } yield b + "-" + c
-   * 
- */ - final def map[A](f: T ⇒ A)(implicit timeout: Timeout): Future[A] = { - val future = DefaultPromise[A](timeout) - onComplete { - _.value.get match { - case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, A]] - case Right(res) ⇒ - future complete (try { - Right(f(res)) - } catch { - case e: Exception ⇒ - //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.map", e.getMessage)) - Left(e) - }) - } - } - future - } - - /** - * Creates a new Future[A] which is completed with this Future's result if - * that conforms to A's erased type or a ClassCastException otherwise. - */ - final def mapTo[A](implicit m: Manifest[A], timeout: Timeout = this.timeout): Future[A] = { - val fa = DefaultPromise[A](timeout) - onComplete { ft ⇒ - fa complete (ft.value.get match { - case l: Left[_, _] ⇒ l.asInstanceOf[Either[Throwable, A]] - case Right(t) ⇒ - try { - Right(BoxedType(m.erasure).cast(t).asInstanceOf[A]) - } catch { - case e: ClassCastException ⇒ Left(e) - } - }) - } - fa - } - - /** - * Creates a new Future by applying a function to the successful result of - * this Future, and returns the result of the function as the new Future. - * If this Future is completed with an exception then the new Future will - * also contain this exception. - * Example: - *
-   * val future1 = for {
-   *   a: Int    <- actor ? "Hello" // returns 5
-   *   b: String <- actor ? a       // returns "10"
-   *   c: String <- actor ? 7       // returns "14"
-   * } yield b + "-" + c
-   * 
- */ - final def flatMap[A](f: T ⇒ Future[A])(implicit timeout: Timeout): Future[A] = { - val future = DefaultPromise[A](timeout) - - onComplete { - _.value.get match { - case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, A]] - case Right(r) ⇒ try { - future.completeWith(f(r)) - } catch { - case e: Exception ⇒ - //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.flatMap", e.getMessage)) - future complete Left(e) - } - } - } - future - } - - final def foreach(f: T ⇒ Unit): Unit = onComplete { - _.value.get match { - case Right(r) ⇒ f(r) - case _ ⇒ - } - } - - final def withFilter(p: T ⇒ Boolean)(implicit timeout: Timeout) = new FutureWithFilter[T](this, p) - - final class FutureWithFilter[+A](self: Future[A], p: A ⇒ Boolean)(implicit timeout: Timeout) { - def foreach(f: A ⇒ Unit): Unit = self filter p foreach f - def map[B](f: A ⇒ B): Future[B] = self filter p map f - def flatMap[B](f: A ⇒ Future[B]): Future[B] = self filter p flatMap f - def withFilter(q: A ⇒ Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) - } - - final def filter(p: T ⇒ Boolean)(implicit timeout: Timeout): Future[T] = { - val future = DefaultPromise[T](timeout) - onComplete { - _.value.get match { - case l: Left[_, _] ⇒ future complete l.asInstanceOf[Either[Throwable, T]] - case r @ Right(res) ⇒ future complete (try { - if (p(res)) r else Left(new MatchError(res)) - } catch { - case e: Exception ⇒ - //dispatcher.prerequisites.eventStream.publish(Error(e, "Future.filter", e.getMessage)) - Left(e) - }) - } - } - future - } - - /** - * Returns the current result, throws the exception if one has been raised, else returns None - */ - final def resultOrException: Option[T] = value match { - case Some(Left(e)) ⇒ throw e - case Some(Right(r)) ⇒ Some(r) - case _ ⇒ None - } - */ -//} - -/** - * Essentially this is the Promise (or write-side) of a Future (read-side). - */ -//trait Promise[T] extends Future[T] { - - /* - def start(): Unit - - /** - * Completes this Future with the specified result, if not already completed. - * @return this - */ - def complete(value: Either[Throwable, T]): this.type - - /** - * Completes this Future with the specified result, if not already completed. - * @return this - */ - final def completeWithResult(result: T): this.type = complete(Right(result)) - - /** - * Completes this Future with the specified exception, if not already completed. - * @return this - */ - final def completeWithException(exception: Throwable): this.type = complete(Left(exception)) - - /** - * Completes this Future with the specified other Future, when that Future is completed, - * unless this Future has already been completed. - * @return this. - */ - final def completeWith(other: Future[T]): this.type = { - other onComplete { f ⇒ complete(f.value.get) } - this - } - */ -/* - final def <<(value: T): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ cont(complete(Right(value))) } - - final def <<(other: Future[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ - val fr = DefaultPromise[Any](this.timeout) - this completeWith other onComplete { f ⇒ - try { - fr completeWith cont(f) - } catch { - case e: Exception ⇒ - //dispatcher.prerequisites.eventStream.publish(Error(e, "Promise.completeWith", e.getMessage)) - fr completeWithException e - } - } - fr - } - - final def <<(stream: PromiseStreamOut[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ - val fr = DefaultPromise[Any](this.timeout) - stream.dequeue(this).onComplete { f ⇒ - try { - fr completeWith cont(f) - } catch { - case e: Exception ⇒ - //dispatcher.prerequisites.eventStream.publish(Error(e, "Promise.completeWith", e.getMessage)) - fr completeWithException e - } - } - fr - } -*/ -//} - -/** - * Represents the internal state of the DefaultCompletableFuture - */ -sealed abstract class FState[+T] { def value: Option[Either[Throwable, T]] } - -case class Pending[T](listeners: List[Future[T] ⇒ Unit] = Nil) extends FState[T] { - def value: Option[Either[Throwable, T]] = None -} - -case class Success[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { - def result: T = value.get.right.get -} - -case class Failure[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { - def exception: Throwable = value.get.left.get -} - -case object Expired extends FState[Nothing] { - def value: Option[Either[Throwable, Nothing]] = None -} - -//Companion object to FState, just to provide a cheap, immutable default entry -private[concurrent] object FState { - def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] - private val emptyPendingValue = Pending[Nothing](Nil) -} - -private[concurrent] object DefaultPromise { - def apply[T](within: Timeout)(implicit disp: MessageDispatcher): Promise[T] = new DefaultPromise[T] { - val timeout = within - implicit val dispatcher = disp - } - - def apply[T]()(implicit dispatcher: MessageDispatcher, timeout: Timeout): Promise[T] = apply(timeout) - - def apply[T](timeout: Long)(implicit dispatcher: MessageDispatcher): Promise[T] = apply(Timeout(timeout)) - - def apply[T](timeout: Long, timeunit: TimeUnit)(implicit dispatcher: MessageDispatcher): Promise[T] = apply(Timeout(timeout, timeunit)) -} - -/** - * The default concrete Future implementation. - */ -trait DefaultPromise[T] extends /*AbstractPromise with*/ Promise[T] { - self ⇒ - -// @volatile private _ref: AnyRef = DefaultPromise.EmptyPending() - -// protected final static AtomicReferenceFieldUpdater updater = -// AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); - - val timeout: Timeout - implicit val dispatcher: MessageDispatcher - - private val _startTimeInNanos = currentTimeInNanos - - @tailrec - private def awaitUnsafe(waitTimeNanos: Long): Boolean = { - if (value.isEmpty && waitTimeNanos > 0) { - val ms = NANOS.toMillis(waitTimeNanos) - val ns = (waitTimeNanos % 1000000l).toInt //As per object.wait spec - val start = currentTimeInNanos - try { synchronized { if (value.isEmpty) wait(ms, ns) } } catch { case e: InterruptedException ⇒ } - - awaitUnsafe(waitTimeNanos - (currentTimeInNanos - start)) - } else { - value.isDefined - } - } - - def await(atMost: Duration): this.type = if (value.isDefined) this else { - Future.blocking() - - val waitNanos = - if (timeout.duration.isFinite && atMost.isFinite) - atMost.toNanos min timeLeft() - else if (atMost.isFinite) - atMost.toNanos - else if (timeout.duration.isFinite) - timeLeft() - else Long.MaxValue //If both are infinite, use Long.MaxValue - - if (awaitUnsafe(waitNanos)) this - else throw new FutureTimeoutException("Futures timed out after [" + NANOS.toMillis(waitNanos) + "] milliseconds") - } - - def await = await(timeout.duration) - - def isExpired: Boolean = if (timeout.duration.isFinite) timeLeft() <= 0 else false - - def value: Option[Either[Throwable, T]] = getState.value - - @inline - protected final def updateState(oldState: FState[T], newState: FState[T]): Boolean = - AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]].compareAndSet(this, oldState, newState) - - @inline - protected final def getState: FState[T] = { - - @tailrec - def read(): FState[T] = { - val cur = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]].get(this) - if (cur.isInstanceOf[Pending[_]] && isExpired) { - if (updateState(cur, Expired)) Expired else read() - } else cur - } - - read() - } - - def complete(value: Either[Throwable, T]): this.type = { - val callbacks = { - try { - @tailrec - def tryComplete: List[Future[T] ⇒ Unit] = { - val cur = getState - - cur match { - case Pending(listeners) ⇒ - if (updateState(cur, if (value.isLeft) Failure(Some(value)) else Success(Some(value)))) listeners - else tryComplete - case _ ⇒ Nil - } - } - tryComplete - } finally { - synchronized { notifyAll() } //Notify any evil blockers - } - } - - if (callbacks.nonEmpty) Future.dispatchTask(() ⇒ callbacks foreach notifyCompleted) - - this - } - - def onComplete(func: Future[T] ⇒ Unit): this.type = { - @tailrec //Returns whether the future has already been completed or not - def tryAddCallback(): Boolean = { - val cur = getState - cur match { - case _: Success[_] | _: Failure[_] ⇒ true - case Expired ⇒ false - case p: Pending[_] ⇒ - val pt = p.asInstanceOf[Pending[T]] - if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false - else tryAddCallback() - } - } - - if (tryAddCallback()) Future.dispatchTask(() ⇒ notifyCompleted(func)) - - this - } - - def onTimeout(func: Future[T] ⇒ Unit): this.type = { - val runNow = - if (!timeout.duration.isFinite) false //Not possible - else if (value.isEmpty) { - if (!isExpired) { - val runnable = new Runnable { - def run() { - if (!isCompleted) { - if (!isExpired) { - // TODO FIXME: add scheduler - //dispatcher.prerequisites.scheduler.scheduleOnce(this, timeLeftNoinline(), NANOS) - } else func(DefaultPromise.this) - } - } - } - /* - TODO FIXME: add scheduler - val timeoutFuture = dispatcher.prerequisites.scheduler.scheduleOnce(runnable, timeLeft(), NANOS) - onComplete(_ ⇒ timeoutFuture.cancel()) - */ - false - } else true - } else false - - if (runNow) Future.dispatchTask(() ⇒ notifyCompleted(func)) - - this - } - - final def orElse[A >: T](fallback: ⇒ A): Future[A] = - if (timeout.duration.isFinite) { - getState match { - case _: Success[_] | _: Failure[_] ⇒ this - case Expired ⇒ Future[A](fallback, timeout) - case _: Pending[_] ⇒ - val promise = DefaultPromise[A](Timeout.never) //TODO FIXME We can't have infinite timeout here, doesn't make sense. - promise completeWith this - val runnable = new Runnable { - def run() { - if (!isCompleted) { - if (!isExpired) { - // TODO FIXME add scheduler - //dispatcher.prerequisites.scheduler.scheduleOnce(this, timeLeftNoinline(), NANOS) - } else promise complete (try { Right(fallback) } catch { case e ⇒ Left(e) }) - } - } - } - // TODO FIXME add - //dispatcher.prerequisites.scheduler.scheduleOnce(runnable, timeLeft(), NANOS) - promise - } - } else this - - private def notifyCompleted(func: Future[T] ⇒ Unit) { - try { func(this) } catch { case e ⇒ /*dispatcher.prerequisites.eventStream.publish(Error(e, "Future", "Future onComplete-callback raised an exception"))*/ } //TODO catch, everything? Really? - } - - @inline - private def currentTimeInNanos: Long = MILLIS.toNanos(System.currentTimeMillis) //TODO Switch to math.abs(System.nanoTime)? - //TODO: the danger of Math.abs is that it could break the ordering of time. So I would not recommend an abs. - @inline - private def timeLeft(): Long = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos) - - private def timeLeftNoinline(): Long = timeLeft() -//} - -/** - * An already completed Future is seeded with it's result at creation, is useful for when you are participating in - * a Future-composition but you already have a value to contribute. - */ -sealed class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val dispatcher: MessageDispatcher) extends Promise[T] { - val value = Some(suppliedValue) - - def complete(value: Either[Throwable, T]): this.type = this - def onComplete(func: Future[T] ⇒ Unit): this.type = { - Future dispatchTask (() ⇒ func(this)) - this - } - def await(atMost: Duration): this.type = this - def await: this.type = this - def isExpired: Boolean = true - def timeout: Timeout = Timeout.zero - - final def onTimeout(func: Future[T] ⇒ Unit): this.type = this - final def orElse[A >: T](fallback: ⇒ A): Future[A] = this - */ -//} - -object BoxedType { - - private val toBoxed = Map[Class[_], Class[_]]( - classOf[Boolean] -> classOf[jl.Boolean], - classOf[Byte] -> classOf[jl.Byte], - classOf[Char] -> classOf[jl.Character], - classOf[Short] -> classOf[jl.Short], - classOf[Int] -> classOf[jl.Integer], - classOf[Long] -> classOf[jl.Long], - classOf[Float] -> classOf[jl.Float], - classOf[Double] -> classOf[jl.Double], - classOf[Unit] -> classOf[scala.runtime.BoxedUnit]) - - def apply(c: Class[_]): Class[_] = { - if (c.isPrimitive) toBoxed(c) else c - } - -} diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 41a41dd611..43abe566de 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -10,7 +10,6 @@ package scala.concurrent -import scala.util.Timeout diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 23f26dd3b5..ee8f484379 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -6,13 +6,14 @@ ** |/ ** \* */ - - package scala +import java.util.concurrent.atomic.{ AtomicInteger } import scala.util.{ Timeout, Duration } +import collection._ +import scala.collection.generic.CanBuildFrom @@ -129,6 +130,81 @@ package concurrent { def this(origin: Future[_]) = this(origin, "Future timed out.") } + trait ExecutionContextBase { + self: ExecutionContext => + + private implicit val executionContext = self + + /** TODO some docs + * + */ + def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { + val buffer = new mutable.ArrayBuffer[T] + val counter = new AtomicInteger(1) // how else could we do this? + val p: Promise[Coll[T]] = promise[Coll[T]] // we need an implicit execctx in the signature + var idx = 0 + + def tryFinish() = if (counter.decrementAndGet() == 0) { + val builder = cbf(futures) + builder ++= buffer + p success builder.result + } + + for (f <- futures) { + val currentIndex = idx + buffer += null.asInstanceOf[T] + counter.incrementAndGet() + f onComplete { + case Left(t) => + p tryFailure t + case Right(v) => + buffer(currentIndex) = v + tryFinish() + } + idx += 1 + } + + tryFinish() + + p.future + } + + /** TODO some docs + * + */ + def any[T](futures: Traversable[Future[T]]): Future[T] = { + val p = promise[T] + val completeFirst: Either[Throwable, T] => Unit = elem => p tryComplete elem + + futures foreach (_ onComplete completeFirst) + + p.future + } + + /** TODO some docs + * + */ + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { + if (futures.isEmpty) Promise.successful[Option[T]](None).future + else { + val result = promise[Option[T]] + val count = new AtomicInteger(futures.size) + val search: Either[Throwable, T] => Unit = { + v => v match { + case Right(r) => if (predicate(r)) result trySuccess Some(r) + case _ => + } + if (count.decrementAndGet() == 0) result trySuccess None + } + + futures.foreach(_ onComplete search) + + result.future + } + } + + } + } diff --git a/src/library/scala/concurrent/package.scala.disabled b/src/library/scala/concurrent/package.scala.disabled deleted file mode 100644 index 42b4bf954c..0000000000 --- a/src/library/scala/concurrent/package.scala.disabled +++ /dev/null @@ -1,108 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - - -package scala - - - - -/** This package object contains primitives for parallel programming. - */ -package object concurrent { - - /** Performs a call which can potentially block execution. - * - * Example: - * {{{ - * val lock = new ReentrantLock - * - * // ... do something ... - * - * blocking { - * if (!lock.hasLock) lock.lock() - * } - * }}} - * - * '''Note:''' calling methods that wait arbitrary amounts of time - * (e.g. for I/O operations or locks) may severely decrease performance - * or even result in deadlocks. This does not include waiting for - * results of futures. - * - * @tparam T the result type of the blocking operation - * @param body the blocking operation - * @param runner the runner used for parallel computations - * @return the result of the potentially blocking operation - */ - def blocking[T](body: =>T)(implicit runner: TaskRunner): T = { - null.asInstanceOf[T] - } - - /** Invokes a computation asynchronously. Does not wait for the computation - * to finish. - * - * @tparam U the result type of the operation - * @param p the computation to be invoked asynchronously - * @param runner the runner used for parallel computations - */ - def spawn[U](p: =>U)(implicit runner: TaskRunner): Unit = { - } - - /** Starts 2 parallel computations and returns once they are completed. - * - * $invokingPar - * - * @tparam T1 the type of the result of 1st the parallel computation - * @tparam T2 the type of the result of 2nd the parallel computation - * @param b1 the 1st computation to be invoked in parallel - * @param b2 the 2nd computation to be invoked in parallel - * @param runner the runner used for parallel computations - * @return a tuple of results corresponding to parallel computations - */ - def par[T1, T2](b1: =>T1)(b2: =>T2)(implicit runner: TaskRunner): (T1, T2) = { - null - } - - /** Starts 3 parallel computations and returns once they are completed. - * - * $invokingPar - * - * @tparam T1 the type of the result of 1st the parallel computation - * @tparam T2 the type of the result of 2nd the parallel computation - * @tparam T3 the type of the result of 3rd the parallel computation - * @param b1 the 1st computation to be invoked in parallel - * @param b2 the 2nd computation to be invoked in parallel - * @param b3 the 3rd computation to be invoked in parallel - * @param runner the runner used for parallel computations - * @return a tuple of results corresponding to parallel computations - */ - def par[T1, T2, T3](b1: =>T1)(b2: =>T2)(b3: =>T3)(implicit runner: TaskRunner): (T1, T2, T3) = { - null - } - - /** Starts 4 parallel computations and returns once they are completed. - * - * $invokingPar - * - * @tparam T1 the type of the result of 1st the parallel computation - * @tparam T2 the type of the result of 2nd the parallel computation - * @tparam T3 the type of the result of 3rd the parallel computation - * @tparam T4 the type of the result of 4th the parallel computation - * @param b1 the 1st computation to be invoked in parallel - * @param b2 the 2nd computation to be invoked in parallel - * @param b3 the 3rd computation to be invoked in parallel - * @param b4 the 4th computation to be invoked in parallel - * @param runner the runner used for parallel computations - * @return a tuple of results corresponding to parallel computations - */ - def par[T1, T2, T3, T4](b1: =>T1)(b2: =>T2)(b3: =>T3)(b4: =>T4)(implicit runner: TaskRunner): (T1, T2, T3, T4) = { - null - } - -} -- cgit v1.2.3 From ebc3636a1b6dd64425630afa83eb398a8d7c43a4 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 19 Jan 2012 19:34:30 +0100 Subject: Deprecating some traits in scala.concurrent. --- src/library/scala/concurrent/Channel.scala | 5 +++-- src/library/scala/concurrent/DelayedLazyVal.scala | 8 +++++--- src/library/scala/concurrent/Future.scala | 2 ++ src/library/scala/concurrent/FutureTaskRunner.scala | 1 + src/library/scala/concurrent/JavaConversions.scala | 7 +++++++ src/library/scala/concurrent/ManagedBlocker.scala | 1 + src/library/scala/concurrent/TaskRunner.scala | 1 + src/library/scala/concurrent/TaskRunners.scala | 1 + src/library/scala/concurrent/ThreadPoolRunner.scala | 1 + src/library/scala/concurrent/ops.scala | 1 + 10 files changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Channel.scala b/src/library/scala/concurrent/Channel.scala index 43d684641e..e79f76430f 100644 --- a/src/library/scala/concurrent/Channel.scala +++ b/src/library/scala/concurrent/Channel.scala @@ -23,7 +23,7 @@ class Channel[A] { private var written = new LinkedList[A] // FIFO buffer, realized through private var lastWritten = written // aliasing of a linked list private var nreaders = 0 - + /** * @param x ... */ @@ -33,7 +33,7 @@ class Channel[A] { lastWritten = lastWritten.next if (nreaders > 0) notify() } - + def read: A = synchronized { while (written.next == null) { try { @@ -46,4 +46,5 @@ class Channel[A] { written = written.next x } + } diff --git a/src/library/scala/concurrent/DelayedLazyVal.scala b/src/library/scala/concurrent/DelayedLazyVal.scala index 391ba7e314..0b7f54a27a 100644 --- a/src/library/scala/concurrent/DelayedLazyVal.scala +++ b/src/library/scala/concurrent/DelayedLazyVal.scala @@ -26,21 +26,23 @@ package scala.concurrent class DelayedLazyVal[T](f: () => T, body: => Unit) { @volatile private[this] var _isDone = false private[this] lazy val complete = f() - + /** Whether the computation is complete. * * @return true if the computation is complete. */ def isDone = _isDone - + /** The current result of f(), or the final result if complete. * * @return the current value */ def apply(): T = if (isDone) complete else f() - + + // TODO replace with scala.concurrent.future { ... } ops.future { body _isDone = true } + } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index ff7da8433a..e68e6077bb 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -41,6 +41,8 @@ import scala.collection.generic.CanBuildFrom * } * }}} * + * @author Philipp Haller, Heather Miller, Aleksandar Prokopec, Viktor Klang + * * @define multipleCallbacks * Multiple callbacks may be registered; there is no guarantee that they will be * executed in a particular order. diff --git a/src/library/scala/concurrent/FutureTaskRunner.scala b/src/library/scala/concurrent/FutureTaskRunner.scala index c5fcde2d19..75e6299ad9 100644 --- a/src/library/scala/concurrent/FutureTaskRunner.scala +++ b/src/library/scala/concurrent/FutureTaskRunner.scala @@ -13,6 +13,7 @@ package scala.concurrent * * @author Philipp Haller */ +@deprecated("Use `ExecutionContext`s instead.", "2.10.0") trait FutureTaskRunner extends TaskRunner { /** The type of the futures that the underlying task runner supports. diff --git a/src/library/scala/concurrent/JavaConversions.scala b/src/library/scala/concurrent/JavaConversions.scala index db3c490882..bac9d4f558 100644 --- a/src/library/scala/concurrent/JavaConversions.scala +++ b/src/library/scala/concurrent/JavaConversions.scala @@ -17,6 +17,7 @@ import java.util.concurrent.{ExecutorService, Executor} */ object JavaConversions { + @deprecated("Use `asExecutionContext` instead.", "2.10.0") implicit def asTaskRunner(exec: ExecutorService): FutureTaskRunner = new ThreadPoolRunner { override protected def executor = @@ -26,6 +27,7 @@ object JavaConversions { exec.shutdown() } + @deprecated("Use `asExecutionContext` instead.", "2.10.0") implicit def asTaskRunner(exec: Executor): TaskRunner = new TaskRunner { type Task[T] = Runnable @@ -46,4 +48,9 @@ object JavaConversions { // do nothing } } + + implicit def asExecutionContext(exec: ExecutorService): ExecutionContext = null // TODO + + implicit def asExecutionContext(exec: Executor): ExecutionContext = null // TODO + } diff --git a/src/library/scala/concurrent/ManagedBlocker.scala b/src/library/scala/concurrent/ManagedBlocker.scala index 9c6f4d51d6..0b6d82e76f 100644 --- a/src/library/scala/concurrent/ManagedBlocker.scala +++ b/src/library/scala/concurrent/ManagedBlocker.scala @@ -12,6 +12,7 @@ package scala.concurrent * * @author Philipp Haller */ +@deprecated("Not used.", "2.10.0") trait ManagedBlocker { /** diff --git a/src/library/scala/concurrent/TaskRunner.scala b/src/library/scala/concurrent/TaskRunner.scala index 64e62adfd3..500d79e07f 100644 --- a/src/library/scala/concurrent/TaskRunner.scala +++ b/src/library/scala/concurrent/TaskRunner.scala @@ -12,6 +12,7 @@ package scala.concurrent * * @author Philipp Haller */ +@deprecated("Use `ExecutionContext`s instead.", "2.10.0") trait TaskRunner { type Task[T] diff --git a/src/library/scala/concurrent/TaskRunners.scala b/src/library/scala/concurrent/TaskRunners.scala index 588073dc5e..7994255b25 100644 --- a/src/library/scala/concurrent/TaskRunners.scala +++ b/src/library/scala/concurrent/TaskRunners.scala @@ -14,6 +14,7 @@ import java.util.concurrent.{ThreadPoolExecutor, LinkedBlockingQueue, TimeUnit} * * @author Philipp Haller */ +@deprecated("Use `ExecutionContext`s instead.", "2.10.0") object TaskRunners { implicit val threadRunner: FutureTaskRunner = diff --git a/src/library/scala/concurrent/ThreadPoolRunner.scala b/src/library/scala/concurrent/ThreadPoolRunner.scala index 27d8f2cc32..a3e0253634 100644 --- a/src/library/scala/concurrent/ThreadPoolRunner.scala +++ b/src/library/scala/concurrent/ThreadPoolRunner.scala @@ -15,6 +15,7 @@ import java.util.concurrent.{ExecutorService, Callable, TimeUnit} * * @author Philipp Haller */ +@deprecated("Use `ExecutionContext`s instead.", "2.10.0") trait ThreadPoolRunner extends FutureTaskRunner { type Task[T] = Callable[T] with Runnable diff --git a/src/library/scala/concurrent/ops.scala b/src/library/scala/concurrent/ops.scala index 92220a8313..2cea29aefe 100644 --- a/src/library/scala/concurrent/ops.scala +++ b/src/library/scala/concurrent/ops.scala @@ -15,6 +15,7 @@ import scala.util.control.Exception.allCatch * * @author Martin Odersky, Stepan Koltsov, Philipp Haller */ +@deprecated("Use `future` instead.", "2.10.0") object ops { val defaultRunner: FutureTaskRunner = TaskRunners.threadRunner -- cgit v1.2.3 From 778e7d1a1b87431449cbe7335ca3a66fbe7c8366 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 19 Jan 2012 19:55:41 +0100 Subject: Add implicit conversion for futures that enables calling nondeterministic methods. --- .../scala/concurrent/ExecutionContext.scala | 77 +++++++++++++++- src/library/scala/concurrent/Future.scala | 32 +------ src/library/scala/concurrent/Promise.scala | 4 +- src/library/scala/concurrent/package.scala | 100 +++++++-------------- 4 files changed, 113 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index ebd5bf6bd3..0657121de2 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -10,13 +10,16 @@ package scala.concurrent +import java.util.concurrent.atomic.{ AtomicInteger } import java.util.concurrent.{ Executors, Future => JFuture, Callable } import scala.util.Duration import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } +import scala.collection.generic.CanBuildFrom +import collection._ -trait ExecutionContext extends ExecutionContextBase { +trait ExecutionContext { protected implicit object CanAwaitEvidence extends CanAwait @@ -34,6 +37,78 @@ trait ExecutionContext extends ExecutionContextBase { def blocking[T](awaitable: Awaitable[T], atMost: Duration): T + /* implementations follow */ + + private implicit val executionContext = this + + /** TODO some docs + * + */ + def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { + val buffer = new mutable.ArrayBuffer[T] + val counter = new AtomicInteger(1) // how else could we do this? + val p: Promise[Coll[T]] = promise[Coll[T]] // we need an implicit execctx in the signature + var idx = 0 + + def tryFinish() = if (counter.decrementAndGet() == 0) { + val builder = cbf(futures) + builder ++= buffer + p success builder.result + } + + for (f <- futures) { + val currentIndex = idx + buffer += null.asInstanceOf[T] + counter.incrementAndGet() + f onComplete { + case Left(t) => + p tryFailure t + case Right(v) => + buffer(currentIndex) = v + tryFinish() + } + idx += 1 + } + + tryFinish() + + p.future + } + + /** TODO some docs + * + */ + def any[T](futures: Traversable[Future[T]]): Future[T] = { + val p = promise[T] + val completeFirst: Either[Throwable, T] => Unit = elem => p tryComplete elem + + futures foreach (_ onComplete completeFirst) + + p.future + } + + /** TODO some docs + * + */ + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { + if (futures.isEmpty) Promise.successful[Option[T]](None).future + else { + val result = promise[Option[T]] + val count = new AtomicInteger(futures.size) + val search: Either[Throwable, T] => Unit = { + v => v match { + case Right(r) => if (predicate(r)) result trySuccess Some(r) + case _ => + } + if (count.decrementAndGet() == 0) result trySuccess None + } + + futures.foreach(_ onComplete search) + + result.future + } + } + } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index e68e6077bb..92e50b1f89 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -77,9 +77,6 @@ import scala.collection.generic.CanBuildFrom * {{{ * f flatMap { (x: Int) => g map { (y: Int) => x + y } } * }}} - * - * @define nonDeterministic - * Note: using this method yields nondeterministic dataflow programs. */ trait Future[+T] extends Awaitable[T] { self => @@ -336,6 +333,8 @@ self => * * Using this method will not cause concurrent programs to become nondeterministic. * + * + * * Example: * {{{ * val f = future { sys.error("failed") } @@ -358,33 +357,6 @@ self => p.future } - /** Creates a new future which holds the result of either this future or `that` future, depending on - * which future was completed first. - * - * $nonDeterministic - * - * Example: - * {{{ - * val f = future { sys.error("failed") } - * val g = future { 5 } - * val h = f any g - * await(0) h // evaluates to either 5 or throws a runtime exception - * }}} - */ - def either[U >: T](that: Future[U]): Future[U] = { - val p = newPromise[U] - - val completePromise: PartialFunction[Either[Throwable, U], _] = { - case Left(t) => p tryFailure t - case Right(v) => p trySuccess v - } - - this onComplete completePromise - that onComplete completePromise - - p.future - } - } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 43abe566de..e9b83cba73 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -34,8 +34,6 @@ trait Promise[T] { */ def future: Future[T] - private def throwCompleted = throw new IllegalStateException("Promise already completed.") - /** Completes the promise with either an exception or a value. * * @param result Either the value or the exception to complete the promise with. @@ -106,6 +104,8 @@ trait Promise[T] { case _ => new ExecutionException(t) } + private def throwCompleted = throw new IllegalStateException("Promise already completed.") + } diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index ee8f484379..73da8469e6 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -10,10 +10,7 @@ package scala -import java.util.concurrent.atomic.{ AtomicInteger } -import scala.util.{ Timeout, Duration } -import collection._ -import scala.collection.generic.CanBuildFrom +import scala.util.Duration @@ -114,6 +111,19 @@ package object concurrent { } } + /** Importing this object allows using some concurrency primitives + * on futures and promises that can yield nondeterministic programs. + * + * While program determinism is broken when using these primitives, + * some programs cannot be written without them (e.g. multiple client threads + * cannot send requests to a server thread through regular promises and futures). + */ + object nondeterministic { + + implicit def future2nondeterministic[T](f: Future[T]) = new NondeterministicFuture[T](f) + + } + } @@ -130,79 +140,35 @@ package concurrent { def this(origin: Future[_]) = this(origin, "Future timed out.") } - trait ExecutionContextBase { - self: ExecutionContext => + private[concurrent] class NondeterministicFuture[+T](self: Future[T]) { - private implicit val executionContext = self - - /** TODO some docs + /** Creates a new future which holds the result of either this future or `that` future, depending on + * which future was completed first. + * + * $nonDeterministic * + * Example: + * {{{ + * val f = future { sys.error("failed") } + * val g = future { 5 } + * val h = f either g + * await(0) h // evaluates to either 5 or throws a runtime exception + * }}} */ - def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { - val buffer = new mutable.ArrayBuffer[T] - val counter = new AtomicInteger(1) // how else could we do this? - val p: Promise[Coll[T]] = promise[Coll[T]] // we need an implicit execctx in the signature - var idx = 0 - - def tryFinish() = if (counter.decrementAndGet() == 0) { - val builder = cbf(futures) - builder ++= buffer - p success builder.result - } + def either[U >: T](that: Future[U]): Future[U] = { + val p = self.newPromise[U] - for (f <- futures) { - val currentIndex = idx - buffer += null.asInstanceOf[T] - counter.incrementAndGet() - f onComplete { - case Left(t) => - p tryFailure t - case Right(v) => - buffer(currentIndex) = v - tryFinish() - } - idx += 1 + val completePromise: PartialFunction[Either[Throwable, U], _] = { + case Left(t) => p tryFailure t + case Right(v) => p trySuccess v } - tryFinish() + self onComplete completePromise + that onComplete completePromise p.future } - /** TODO some docs - * - */ - def any[T](futures: Traversable[Future[T]]): Future[T] = { - val p = promise[T] - val completeFirst: Either[Throwable, T] => Unit = elem => p tryComplete elem - - futures foreach (_ onComplete completeFirst) - - p.future - } - - /** TODO some docs - * - */ - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { - if (futures.isEmpty) Promise.successful[Option[T]](None).future - else { - val result = promise[Option[T]] - val count = new AtomicInteger(futures.size) - val search: Either[Throwable, T] => Unit = { - v => v match { - case Right(r) => if (predicate(r)) result trySuccess Some(r) - case _ => - } - if (count.decrementAndGet() == 0) result trySuccess None - } - - futures.foreach(_ onComplete search) - - result.future - } - } - } } -- cgit v1.2.3 From f58ade23be8354aab223da5ca1e7162b6b53749b Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 19 Jan 2012 20:33:01 +0100 Subject: Add NonDeterministic evidence needed to call nondeterministic methods. --- .../scala/concurrent/ExecutionContext.scala | 20 ++++++++-- src/library/scala/concurrent/Future.scala | 43 ++++++++++++++++++---- src/library/scala/concurrent/Promise.scala | 30 +++++++++++---- src/library/scala/concurrent/akka/Promise.scala | 6 +-- .../scala/concurrent/default/TaskImpl.scala | 7 ++-- src/library/scala/concurrent/package.scala | 39 ++++---------------- 6 files changed, 91 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 0657121de2..260d4cb54d 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -16,6 +16,7 @@ import scala.util.Duration import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } import scala.collection.generic.CanBuildFrom import collection._ +import annotation.implicitNotFound @@ -41,10 +42,21 @@ trait ExecutionContext { private implicit val executionContext = this + def keptPromise[T](result: T): Promise[T] = { + val p = promise[T] + p success result + } + + def brokenPromise[T](t: Throwable): Promise[T] = { + val p = promise[T] + p failure t + } + /** TODO some docs * */ def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { + import nondeterministic._ val buffer = new mutable.ArrayBuffer[T] val counter = new AtomicInteger(1) // how else could we do this? val p: Promise[Coll[T]] = promise[Coll[T]] // we need an implicit execctx in the signature @@ -78,7 +90,8 @@ trait ExecutionContext { /** TODO some docs * */ - def any[T](futures: Traversable[Future[T]]): Future[T] = { + @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") + def any[T](futures: Traversable[Future[T]])(implicit nondet: NonDeterministic): Future[T] = { val p = promise[T] val completeFirst: Either[Throwable, T] => Unit = elem => p tryComplete elem @@ -90,8 +103,9 @@ trait ExecutionContext { /** TODO some docs * */ - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { - if (futures.isEmpty) Promise.successful[Option[T]](None).future + @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit nondet: NonDeterministic): Future[Option[T]] = { + if (futures.isEmpty) Promise.kept[Option[T]](None).future else { val result = promise[Option[T]] val count = new AtomicInteger(futures.size) diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 92e50b1f89..4f89aa483d 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -10,20 +10,21 @@ package scala.concurrent -import scala.util.{ Timeout, Duration } -import scala.Option - import java.util.concurrent.{ ConcurrentLinkedQueue, TimeUnit, Callable } import java.util.concurrent.TimeUnit.{ NANOSECONDS => NANOS, MILLISECONDS ⇒ MILLIS } import java.lang.{ Iterable => JIterable } import java.util.{ LinkedList => JLinkedList } +import java.{ lang => jl } +import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } + +import scala.util.{ Timeout, Duration } +import scala.Option import scala.annotation.tailrec import scala.collection.mutable.Stack -import java.{ lang => jl } -import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } import scala.collection.mutable.Builder import scala.collection.generic.CanBuildFrom +import scala.annotation.implicitNotFound @@ -357,6 +358,34 @@ self => p.future } + /** Creates a new future which holds the result of either this future or `that` future, depending on + * which future was completed first. + * + * $nonDeterministic + * + * Example: + * {{{ + * val f = future { sys.error("failed") } + * val g = future { 5 } + * val h = f either g + * await(0) h // evaluates to either 5 or throws a runtime exception + * }}} + */ + @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") + def either[U >: T](that: Future[U])(implicit nondet: NonDeterministic): Future[U] = { + val p = self.newPromise[U] + + val completePromise: PartialFunction[Either[Throwable, U], _] = { + case Left(t) => p tryFailure t + case Right(v) => p trySuccess v + } + + self onComplete completePromise + that onComplete completePromise + + p.future + } + } @@ -377,9 +406,9 @@ object Future { // move this to future companion object @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) - def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[T] = ec.any(futures) + def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext, nondet: NonDeterministic): Future[T] = ec.any(futures) - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext): Future[Option[T]] = ec.find(futures)(predicate) + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext, nondet: NonDeterministic): Future[Option[T]] = ec.find(futures)(predicate) } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index e9b83cba73..6aa04eff9f 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -10,6 +10,7 @@ package scala.concurrent +import scala.annotation.implicitNotFound @@ -30,6 +31,8 @@ package scala.concurrent */ trait Promise[T] { + import nondeterministic._ + /** Future containing the value of this promise. */ def future: Future[T] @@ -48,7 +51,8 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryComplete(result: Either[Throwable, T]): Boolean + @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") + def tryComplete(result: Either[Throwable, T])(implicit nondet: NonDeterministic): Boolean /** Completes this promise with the specified future, once that future is completed. * @@ -75,7 +79,8 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def trySuccess(value: T): Boolean = tryComplete(Right(value)) + @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") + def trySuccess(value: T)(implicit nondet: NonDeterministic): Boolean = tryComplete(Right(value))(nonDeterministicEvidence) /** Completes the promise with an exception. * @@ -93,7 +98,8 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryFailure(t: Throwable): Boolean = tryComplete(Left(t)) + @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") + def tryFailure(t: Throwable)(implicit nondet: NonDeterministic): Boolean = tryComplete(Left(t))(nonDeterministicEvidence) /** Wraps a `Throwable` in an `ExecutionException` if necessary. * @@ -112,9 +118,19 @@ trait Promise[T] { object Promise { - def successful[T](result: T): Promise[T] = { - val p = promise[T]() - p.success(result) - } + def kept[T](result: T)(implicit execctx: ExecutionContext): Promise[T] = + execctx keptPromise result + + def broken[T](t: Throwable)(implicit execctx: ExecutionContext): Promise[T] = + execctx brokenPromise t } + + + + + + + + + diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index d688d3d850..e36d237e82 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -16,7 +16,7 @@ import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blockin //import scala.util.continuations._ import scala.util.Duration import scala.annotation.tailrec - +import scala.concurrent.NonDeterministic trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { @@ -142,7 +142,7 @@ object Promise { @inline protected final def getState: FState[T] = updater.get(this) - def tryComplete(value: Either[Throwable, T]): Boolean = { + def tryComplete(value: Either[Throwable, T])(implicit nd: NonDeterministic): Boolean = { val callbacks: List[Either[Throwable, T] => Any] = { try { @tailrec @@ -210,7 +210,7 @@ object Promise { final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { val value = Some(resolve(suppliedValue)) - def tryComplete(value: Either[Throwable, T]): Boolean = false + def tryComplete(value: Either[Throwable, T])(implicit nondet: NonDeterministic): Boolean = false def onComplete[U](func: Either[Throwable, T] => U): this.type = { val completedAs = value.get diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 59037cc48b..771cf02ec1 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -7,6 +7,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } import scala.util.Duration import scala.annotation.tailrec +import scala.concurrent.NonDeterministic @@ -85,12 +86,12 @@ extends Promise[T] with Future[T] with Completable[T] { case _ => null } - def tryComplete(r: Either[Throwable, T]) = r match { + def tryComplete(r: Either[Throwable, T])(implicit nd: NonDeterministic) = r match { case Left(t) => tryFailure(t) case Right(v) => trySuccess(v) } - override def trySuccess(value: T): Boolean = { + override def trySuccess(value: T)(implicit nd: NonDeterministic): Boolean = { val cbs = tryCompleteState(Success(value)) if (cbs == null) false @@ -103,7 +104,7 @@ extends Promise[T] with Future[T] with Completable[T] { } } - override def tryFailure(t: Throwable): Boolean = { + override def tryFailure(t: Throwable)(implicit nd: NonDeterministic): Boolean = { val wrapped = wrap(t) val cbs = tryCompleteState(Failure(wrapped)) if (cbs == null) diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 73da8469e6..3e60ffe8de 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -120,7 +120,7 @@ package object concurrent { */ object nondeterministic { - implicit def future2nondeterministic[T](f: Future[T]) = new NondeterministicFuture[T](f) + implicit val nonDeterministicEvidence = new NonDeterministic {} } @@ -140,36 +140,13 @@ package concurrent { def this(origin: Future[_]) = this(origin, "Future timed out.") } - private[concurrent] class NondeterministicFuture[+T](self: Future[T]) { - - /** Creates a new future which holds the result of either this future or `that` future, depending on - * which future was completed first. - * - * $nonDeterministic - * - * Example: - * {{{ - * val f = future { sys.error("failed") } - * val g = future { 5 } - * val h = f either g - * await(0) h // evaluates to either 5 or throws a runtime exception - * }}} - */ - def either[U >: T](that: Future[U]): Future[U] = { - val p = self.newPromise[U] - - val completePromise: PartialFunction[Either[Throwable, U], _] = { - case Left(t) => p tryFailure t - case Right(v) => p trySuccess v - } - - self onComplete completePromise - that onComplete completePromise - - p.future - } - - } + /** Evidence that the program can be nondeterministic. + * + * Programs in which such an evidence is available in scope + * can contain calls to methods which yield nondeterministic + * programs. + */ + sealed trait NonDeterministic } -- cgit v1.2.3 From da3fce49d2dae2b883a8147c63d8da3b845663d2 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Fri, 20 Jan 2012 19:13:28 +0100 Subject: Add implicit for duration. --- src/library/scala/concurrent/package.scala | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 3e60ffe8de..9f3238ddb2 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -124,6 +124,13 @@ package object concurrent { } + final class DurationOps private[concurrent] (x: Int) { + // TODO ADD OTHERS + def ns = util.Duration.fromNanos(0) + } + + @inline implicit final def int2durationops(x: Int) = new DurationOps(x) + } -- cgit v1.2.3 From 645cb791cf032d464de6a3a0d8e7ee1ae4ffe73c Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 23 Jan 2012 15:20:02 +0100 Subject: Fixed the way callbacks are handled. Removed executor from base future trait. --- src/library/scala/concurrent/Future.scala | 8 ++------ src/library/scala/concurrent/Promise.scala | 4 ++-- src/library/scala/concurrent/akka/Promise.scala | 13 +++++++------ src/library/scala/concurrent/default/TaskImpl.scala | 2 ++ 4 files changed, 13 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 4f89aa483d..a22f67d45d 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -128,13 +128,9 @@ self => /* Miscellaneous */ - /** The execution context of the future. - */ - def executor: ExecutionContext - /** Creates a new promise. */ - def newPromise[S]: Promise[S] = executor promise + def newPromise[S]: Promise[S] /* Projections */ @@ -152,7 +148,7 @@ self => * and throws a corresponding exception if the original future fails. */ def failed: Future[Throwable] = new Future[Throwable] { - def executor = self.executor + def newPromise[S]: Promise[S] = self.newPromise def onComplete[U](func: Either[Throwable, Throwable] => U) = { self.onComplete { case Left(t) => func(Right(t)) diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 6aa04eff9f..669025699a 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -101,8 +101,8 @@ trait Promise[T] { @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") def tryFailure(t: Throwable)(implicit nondet: NonDeterministic): Boolean = tryComplete(Left(t))(nonDeterministicEvidence) - /** Wraps a `Throwable` in an `ExecutionException` if necessary. - * + /** Wraps a `Throwable` in an `ExecutionException` if necessary. TODO replace with `resolver` from scala.concurrent + * * $allowedThrowables */ protected def wrap(t: Throwable): Throwable = t match { diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index e36d237e82..340b40bf74 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -23,6 +23,8 @@ trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { def future = this + def newPromise[S]: Promise[S] = executor promise + // TODO refine answer and return types here from Any to type parameters // then move this up in the hierarchy /* @@ -194,12 +196,11 @@ object Promise { } private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T]) { - // TODO see what to do about logging - //try { - func(result) - //} catch { - // case e => logError("Future onComplete-callback raised an exception", e) - //} + try { + func(result) + } catch { + case e => e.printStackTrace() + } } } diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 771cf02ec1..227d9d48cd 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -16,6 +16,8 @@ self: Future[T] => val executor: ExecutionContextImpl + def newPromise[S]: Promise[S] = executor promise + type Callback = Either[Throwable, T] => Any def getState: State[T] -- cgit v1.2.3 From 85daadef89a9ed5c3901a5822221366ee6746d49 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Tue, 24 Jan 2012 15:22:27 +0100 Subject: Removed the nondeterministic implicit. Added rescue. --- .../scala/concurrent/ExecutionContext.scala | 7 +- src/library/scala/concurrent/Future.scala | 97 ++++++++++++++-------- src/library/scala/concurrent/Promise.scala | 10 +-- src/library/scala/concurrent/akka/Promise.scala | 4 +- .../scala/concurrent/default/TaskImpl.scala | 7 +- src/library/scala/concurrent/package.scala | 3 - 6 files changed, 74 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 260d4cb54d..5ad9265f59 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -16,7 +16,6 @@ import scala.util.Duration import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } import scala.collection.generic.CanBuildFrom import collection._ -import annotation.implicitNotFound @@ -90,8 +89,7 @@ trait ExecutionContext { /** TODO some docs * */ - @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") - def any[T](futures: Traversable[Future[T]])(implicit nondet: NonDeterministic): Future[T] = { + def any[T](futures: Traversable[Future[T]]): Future[T] = { val p = promise[T] val completeFirst: Either[Throwable, T] => Unit = elem => p tryComplete elem @@ -103,8 +101,7 @@ trait ExecutionContext { /** TODO some docs * */ - @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit nondet: NonDeterministic): Future[Option[T]] = { + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { if (futures.isEmpty) Promise.kept[Option[T]](None).future else { val result = promise[Option[T]] diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index a22f67d45d..2ad24c052d 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -24,7 +24,6 @@ import scala.annotation.tailrec import scala.collection.mutable.Stack import scala.collection.mutable.Builder import scala.collection.generic.CanBuildFrom -import scala.annotation.implicitNotFound @@ -60,7 +59,10 @@ import scala.annotation.implicitNotFound * as the cause. * If a future is failed with a `scala.runtime.NonLocalReturnControl`, * it is completed with a value instead from that throwable instead instead. - * + * + * @define nonDeterministic + * Note: using this method yields nondeterministic dataflow programs. + * * @define forComprehensionExamples * Example: * @@ -173,31 +175,6 @@ self => /* Monadic operations */ - /** Creates a new future that will handle any matching throwable that this - * future might contain. If there is no match, or if this future contains - * a valid result then the new future will contain the same. - * - * Example: - * - * {{{ - * future (6 / 0) recover { case e: ArithmeticException ⇒ 0 } // result: 0 - * future (6 / 0) recover { case e: NotFoundException ⇒ 0 } // result: exception - * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3 - * }}} - */ - def recover[U >: T](pf: PartialFunction[Throwable, U]): Future[U] = { - val p = newPromise[U] - - onComplete { - case Left(t) if pf isDefinedAt t => - try { p success pf(t) } - catch { case t: Throwable => p complete resolver(t) } - case otherwise => p complete otherwise - } - - p.future - } - /** Asynchronously processes the value in the future once the value becomes available. * * Will not be called if the future fails. @@ -324,14 +301,69 @@ self => p.future } + /** Creates a new future that will handle any matching throwable that this + * future might contain. If there is no match, or if this future contains + * a valid result then the new future will contain the same. + * + * Example: + * + * {{{ + * future (6 / 0) recover { case e: ArithmeticException ⇒ 0 } // result: 0 + * future (6 / 0) recover { case e: NotFoundException ⇒ 0 } // result: exception + * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3 + * }}} + */ + def recover[U >: T](pf: PartialFunction[Throwable, U]): Future[U] = { + val p = newPromise[U] + + onComplete { + case Left(t) if pf isDefinedAt t => + try { p success pf(t) } + catch { case t: Throwable => p complete resolver(t) } + case otherwise => p complete otherwise + } + + p.future + } + + /** Creates a new future that will handle any matching throwable that this + * future might contain by assigning it a value of another future. + * + * If there is no match, or if this future contains + * a valid result then the new future will contain the same result. + * + * Example: + * + * {{{ + * val f = future { Int.MaxValue } + * future (6 / 0) rescue { case e: ArithmeticException => f } // result: Int.MaxValue + * }}} + */ + def rescue[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = { + val p = newPromise[U] + + onComplete { + case Left(t) if pf isDefinedAt t => + try { + pf(t) onComplete { + case Left(t) => p failure t + case Right(v) => p success v + } + } catch { + case t: Throwable => p complete resolver(t) + } + case otherwise => p complete otherwise + } + + p.future + } + /** Creates a new future which holds the result of this future if it was completed successfully, or, if not, * the result of the `that` future if `that` is completed successfully. * If both futures are failed, the resulting future holds the throwable object of the first future. * * Using this method will not cause concurrent programs to become nondeterministic. * - * - * * Example: * {{{ * val f = future { sys.error("failed") } @@ -367,8 +399,7 @@ self => * await(0) h // evaluates to either 5 or throws a runtime exception * }}} */ - @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") - def either[U >: T](that: Future[U])(implicit nondet: NonDeterministic): Future[U] = { + def either[U >: T](that: Future[U]): Future[U] = { val p = self.newPromise[U] val completePromise: PartialFunction[Either[Throwable, U], _] = { @@ -402,9 +433,9 @@ object Future { // move this to future companion object @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) - def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext, nondet: NonDeterministic): Future[T] = ec.any(futures) + def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[T] = ec.any(futures) - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext, nondet: NonDeterministic): Future[Option[T]] = ec.find(futures)(predicate) + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext): Future[Option[T]] = ec.find(futures)(predicate) } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 669025699a..acc02fba5f 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -10,7 +10,6 @@ package scala.concurrent -import scala.annotation.implicitNotFound @@ -51,8 +50,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") - def tryComplete(result: Either[Throwable, T])(implicit nondet: NonDeterministic): Boolean + def tryComplete(result: Either[Throwable, T]): Boolean /** Completes this promise with the specified future, once that future is completed. * @@ -79,8 +77,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") - def trySuccess(value: T)(implicit nondet: NonDeterministic): Boolean = tryComplete(Right(value))(nonDeterministicEvidence) + def trySuccess(value: T): Boolean = tryComplete(Right(value)) /** Completes the promise with an exception. * @@ -98,8 +95,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - @implicitNotFound(msg = "Calling this method yields non-deterministic programs.") - def tryFailure(t: Throwable)(implicit nondet: NonDeterministic): Boolean = tryComplete(Left(t))(nonDeterministicEvidence) + def tryFailure(t: Throwable): Boolean = tryComplete(Left(t)) /** Wraps a `Throwable` in an `ExecutionException` if necessary. TODO replace with `resolver` from scala.concurrent * diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index 340b40bf74..923d5baf6d 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -144,7 +144,7 @@ object Promise { @inline protected final def getState: FState[T] = updater.get(this) - def tryComplete(value: Either[Throwable, T])(implicit nd: NonDeterministic): Boolean = { + def tryComplete(value: Either[Throwable, T]): Boolean = { val callbacks: List[Either[Throwable, T] => Any] = { try { @tailrec @@ -211,7 +211,7 @@ object Promise { final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { val value = Some(resolve(suppliedValue)) - def tryComplete(value: Either[Throwable, T])(implicit nondet: NonDeterministic): Boolean = false + def tryComplete(value: Either[Throwable, T]): Boolean = false def onComplete[U](func: Either[Throwable, T] => U): this.type = { val completedAs = value.get diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 227d9d48cd..3e52d79894 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -7,7 +7,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } import scala.util.Duration import scala.annotation.tailrec -import scala.concurrent.NonDeterministic @@ -88,12 +87,12 @@ extends Promise[T] with Future[T] with Completable[T] { case _ => null } - def tryComplete(r: Either[Throwable, T])(implicit nd: NonDeterministic) = r match { + def tryComplete(r: Either[Throwable, T]) = r match { case Left(t) => tryFailure(t) case Right(v) => trySuccess(v) } - override def trySuccess(value: T)(implicit nd: NonDeterministic): Boolean = { + override def trySuccess(value: T): Boolean = { val cbs = tryCompleteState(Success(value)) if (cbs == null) false @@ -106,7 +105,7 @@ extends Promise[T] with Future[T] with Completable[T] { } } - override def tryFailure(t: Throwable)(implicit nd: NonDeterministic): Boolean = { + override def tryFailure(t: Throwable): Boolean = { val wrapped = wrap(t) val cbs = tryCompleteState(Failure(wrapped)) if (cbs == null) diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 9f3238ddb2..6c1e323595 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -119,9 +119,6 @@ package object concurrent { * cannot send requests to a server thread through regular promises and futures). */ object nondeterministic { - - implicit val nonDeterministicEvidence = new NonDeterministic {} - } final class DurationOps private[concurrent] (x: Int) { -- cgit v1.2.3 From 6a8db335b0312342f95df9dc87fcc016fed3a463 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Fri, 27 Jan 2012 10:53:17 +0100 Subject: Change the implementation of the future failed projection. --- src/library/scala/concurrent/Future.scala | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 2ad24c052d..29b17cf70a 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -149,27 +149,18 @@ self => * Blocking on this future returns a value if the original future is completed with an exception * and throws a corresponding exception if the original future fails. */ - def failed: Future[Throwable] = new Future[Throwable] { - def newPromise[S]: Promise[S] = self.newPromise - def onComplete[U](func: Either[Throwable, Throwable] => U) = { - self.onComplete { - case Left(t) => func(Right(t)) - case Right(v) => func(Left(noSuchElem(v))) // do nothing - } - this - } - def await(atMost: Duration)(implicit canawait: CanAwait): Throwable = { - var t: Throwable = null - try { - val res = self.await(atMost) - t = noSuchElem(res) - } catch { - case t: Throwable => return t - } - throw t - } - private def noSuchElem(v: T) = + def failed: Future[Throwable] = { + def noSuchElem(v: T) = new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v) + + val p = newPromise[Throwable] + + this onComplete { + case Left(t) => p success t + case Right(v) => p failure noSuchElem(v) + } + + p.future } -- cgit v1.2.3 From 62f012c2bbd948a1922a9eafb13267dc45f8392c Mon Sep 17 00:00:00 2001 From: aleksandar Date: Fri, 27 Jan 2012 11:31:43 +0100 Subject: Implement the ensure and andThen methods on futures. --- src/library/scala/concurrent/Future.scala | 57 ++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 29b17cf70a..1b20c91e49 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -155,7 +155,7 @@ self => val p = newPromise[Throwable] - this onComplete { + onComplete { case Left(t) => p success t case Right(v) => p failure noSuchElem(v) } @@ -377,6 +377,61 @@ self => p.future } + /** Applies the side-effecting function to the result of this future, and returns + * a new future with the result of this future. + * + * This method allows one to enforce that the callbacks are executed in a + * specified order. + * + * Note that if one of the chained `andThen` callbacks throws + * an exception, that exception is not propagated to the subsequent `andThen` + * callbacks. Instead, the subsequent `andThen` callbacks are given the original + * value of this future. + * + * The following example prints out `5`: + * + * {{{ + * val f = future { 5 } + * f andThen { + * case r => sys.error("runtime exception") + * } andThen { + * case Left(t) => println(t) + * case Right(v) => println(v) + * } + * }}} + */ + def andThen[U](pf: PartialFunction[Either[Throwable, T], U]): Future[T] = { + val p = newPromise[T] + + onComplete { + case r => + try if (pf isDefinedAt r) pf(r) + finally p complete r + } + + p.future + } + + /** Executes a piece of code once this future is completed, regardless of whether + * or not the future fails or succeeds, and returns a new future with the result of this + * future. + * + * This method allows one to enforce ordering. + * + * The below example always executes the `println` calls in order: + * {{{ + * val f = future { 5 } + * f ensure { + * println("The value is available.") + * } ensure { + * println("The application can now end.") + * } + * }}} + */ + def ensure[U](body: =>U): Future[T] = andThen { + case _ => body + } + /** Creates a new future which holds the result of either this future or `that` future, depending on * which future was completed first. * -- cgit v1.2.3 From 7d206f3c7350a2d45a06bb49abf64530075d716f Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Mon, 30 Jan 2012 13:03:56 +0100 Subject: Added first cut of Try type, refactored from twitter util. --- src/library/scala/util/Try.scala | 196 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/library/scala/util/Try.scala (limited to 'src') diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala new file mode 100644 index 0000000000..5dc5ede1cc --- /dev/null +++ b/src/library/scala/util/Try.scala @@ -0,0 +1,196 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2008-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + + +package scala.util + +/** + * The Try type represents a computation that may either result in an exception, + * or return a success value. It's analagous to the `Either` type. + * + */ +sealed abstract class Try[+T] { + /** + * Returns true if the Try is a Failure, false otherwise. + */ + def isFailure: Boolean + + /** + * Returns true if the Try is a Success, false otherwise. + */ + def isSuccess: Boolean + + /** + * Returns the value from this Success or the given argument if this is a Failure. + */ + def getOrElse[U >: T](default: => U) = if (isSuccess) apply() else default + + /** + * Returns the value from this Success or throws the exception if this is a Failure + */ + def apply(): T + + /** + * Returns the value from this Success or throws the exception if this is a Failure. + * Alias for apply() + */ + def get = apply() + + /** + * Applies the given function f if this is a Result. + */ + def foreach[U](f: T => U) { onSuccess(f) } + + /** + * Returns the given function applied to the value from this Success or returns this if this is a Failure. + * + */ + def flatMap[U](f: T => Try[U]): Try[U] + + /** + * Maps the given function to the value from this Success or returns this if this is a Failure + */ + def map[U](f: T => U): Try[U] + + /** + * Converts this to a Failure if the predicate is not satisfied. + */ + def filter(p: T => Boolean): Try[T] + + /** + * Calls the exceptionHandler with the exception if this is a Failure. This is like flatMap for the exception. + */ + def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] + + /** + * Calls the exceptionHandler with the exception if this is a Failure. This is like map for the exception. + */ + def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] + + /** + * Invoked only if the computation was successful. + */ + def onSuccess[U](f: T => U): Try[T] + + /** + * Invoked only if the computation failed. + */ + def onFailure[U](rescueException: Throwable => U): Try[T] + + /** + * Invoked regardless of whether the computation completed + * successfully or unsuccessfully. Implemented in terms of + * `respond` so that subclasses control evaluation order. Returns a + * chained `this` as in `respond`. + */ + def ensure[U](f: => U): Try[T] = + respond { _ => f } + + /** + * Returns None if this is a Failure or a Some containing the value if this is a Success + */ + def toOption = if (isSuccess) Some(apply()) else None + + /** + * Invokes the given closure when the value is available. Returns + * another 'This[R]' that is guaranteed to be available only *after* + * 'k' has run. This enables the enforcement of invocation ordering. + * + * This is overridden by subclasses. + */ + def respond[U](k: Try[T] => U): Try[T] = { + k(this) + this + } + + /** + * Invokes the given transformation when the value is available, + * returning the transformed value. This method is like a combination + * of flatMap and rescue. This method is typically used for more + * imperative control-flow than flatMap/rescue which often exploits + * the Null Object Pattern. + * + * This is overridden by subclasses. + */ + def transform[U](f: Try[T] => Try[U]): Try[U] = + f(this) + + /** + * Returns the given function applied to the value from this Success or returns this if this is a Failure. + * Alias for flatMap + */ + def andThen[U](f: T => Try[U]) = flatMap(f) + + /** + * Transforms a nested Try, i.e., a Try of type `Try[Try[T]]`, + * into an un-nested Try, i.e., a Try of type `Try[T]` + */ + def flatten[U](implicit ev: T <:< Try[U]): Try[U] +} + +final case class Failure[+T](e: Throwable) extends Try[T] { + def isFailure = true + def isSuccess = false + def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = { + try { + if (rescueException.isDefinedAt(e)) rescueException(e) else this + } catch { + case e2 => Failure(e2) + } + } + def apply(): T = throw e + def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](e) + def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](e) + def map[U](f: T => U): Try[U] = Failure[U](e) + def filter(p: T => Boolean): Try[T] = this + def onFailure[U](rescueException: Throwable => U): Try[T] = { + rescueException(e) + this + } + def onSuccess[U](f: T => U): Try[T] = this + def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = + if (rescueException.isDefinedAt(e)) { + Try(rescueException(e)) + } else { + this + } +} + +final case class Success[+T](r: T) extends Try[T] { + def isFailure = false + def isSuccess = true + def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(r) + def apply() = r + def flatMap[U](f: T => Try[U]): Try[U] = + try f(r) + catch { + case e => Failure(e) + } + def flatten[U](implicit ev: T <:< Try[U]): Try[U] = r + def map[U](f: T => U): Try[U] = Try[U](f(r)) + def filter(p: T => Boolean): Try[T] = + if (p(apply())) this + else Failure(new NoSuchElementException("Predicate does not hold")) + def onFailure[U](rescueException: Throwable => U): Try[T] = this + def onSuccess[U](f: T => U): Try[T] = { + f(r) + this + } + def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this +} + +object Try { + + def apply[T](r: => T): Try[T] = { + try { Success(r) } catch { + case e => Failure(e) + } + } + +} \ No newline at end of file -- cgit v1.2.3 From 1d733556d04c4586b53e8bdd2d33211ebfb66baa Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 30 Jan 2012 13:58:49 +0100 Subject: Changed the comments a bit, removed on* methods on Try. --- src/library/scala/util/Try.scala | 87 +++++++++++++++------------------------- 1 file changed, 32 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index 5dc5ede1cc..a810195b9f 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -6,83 +6,65 @@ ** |/ ** \* */ +package scala.util -package scala.util /** - * The Try type represents a computation that may either result in an exception, - * or return a success value. It's analagous to the `Either` type. - * + * The `Try` type represents a computation that may either result in an exception, + * or return a success value. It's analagous to the `Either` type. */ sealed abstract class Try[+T] { /** - * Returns true if the Try is a Failure, false otherwise. + * Returns true if the `Try` is a `Failure`, false otherwise. */ def isFailure: Boolean /** - * Returns true if the Try is a Success, false otherwise. + * Returns true if the `Try` is a `Success`, false otherwise. */ def isSuccess: Boolean /** - * Returns the value from this Success or the given argument if this is a Failure. - */ - def getOrElse[U >: T](default: => U) = if (isSuccess) apply() else default - - /** - * Returns the value from this Success or throws the exception if this is a Failure + * Returns the value from this `Success` or the given argument if this is a `Failure`. */ - def apply(): T + def getOrElse[U >: T](default: => U) = if (isSuccess) get else default /** - * Returns the value from this Success or throws the exception if this is a Failure. - * Alias for apply() + * Returns the value from this `Success` or throws the exception if this is a `Failure`. */ - def get = apply() + def get /** * Applies the given function f if this is a Result. */ - def foreach[U](f: T => U) { onSuccess(f) } + def foreach[U](f: T => U): Unit /** - * Returns the given function applied to the value from this Success or returns this if this is a Failure. - * + * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. */ def flatMap[U](f: T => Try[U]): Try[U] /** - * Maps the given function to the value from this Success or returns this if this is a Failure + * Maps the given function to the value from this `Success` or returns this if this is a `Failure`. */ def map[U](f: T => U): Try[U] /** - * Converts this to a Failure if the predicate is not satisfied. + * Converts this to a `Failure` if the predicate is not satisfied. */ def filter(p: T => Boolean): Try[T] /** - * Calls the exceptionHandler with the exception if this is a Failure. This is like flatMap for the exception. + * Calls the exceptionHandler with the exception if this is a `Failure`. This is like `flatMap` for the exception. */ def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] /** - * Calls the exceptionHandler with the exception if this is a Failure. This is like map for the exception. + * Calls the exceptionHandler with the exception if this is a `Failure`. This is like map for the exception. */ def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] - /** - * Invoked only if the computation was successful. - */ - def onSuccess[U](f: T => U): Try[T] - - /** - * Invoked only if the computation failed. - */ - def onFailure[U](rescueException: Throwable => U): Try[T] - /** * Invoked regardless of whether the computation completed * successfully or unsuccessfully. Implemented in terms of @@ -93,9 +75,9 @@ sealed abstract class Try[+T] { respond { _ => f } /** - * Returns None if this is a Failure or a Some containing the value if this is a Success + * Returns `None` if this is a `Failure` or a `Some` containing the value if this is a `Success`. */ - def toOption = if (isSuccess) Some(apply()) else None + def toOption = if (isSuccess) Some(get) else None /** * Invokes the given closure when the value is available. Returns @@ -112,7 +94,7 @@ sealed abstract class Try[+T] { /** * Invokes the given transformation when the value is available, * returning the transformed value. This method is like a combination - * of flatMap and rescue. This method is typically used for more + * of `flatMap` and `rescue`. This method is typically used for more * imperative control-flow than flatMap/rescue which often exploits * the Null Object Pattern. * @@ -122,18 +104,19 @@ sealed abstract class Try[+T] { f(this) /** - * Returns the given function applied to the value from this Success or returns this if this is a Failure. - * Alias for flatMap + * Returns the given function applied to the value from this Success or returns this if this is a `Failure. + * Alias for `flatMap`. */ def andThen[U](f: T => Try[U]) = flatMap(f) /** - * Transforms a nested Try, i.e., a Try of type `Try[Try[T]]`, - * into an un-nested Try, i.e., a Try of type `Try[T]` + * Transforms a nested `Try`, i.e., a `Try` of type `Try[Try[T]]`, + * into an un-nested `Try`, i.e., a `Try` of type `Try[T]` */ def flatten[U](implicit ev: T <:< Try[U]): Try[U] } + final case class Failure[+T](e: Throwable) extends Try[T] { def isFailure = true def isSuccess = false @@ -144,16 +127,12 @@ final case class Failure[+T](e: Throwable) extends Try[T] { case e2 => Failure(e2) } } - def apply(): T = throw e + def get: T = throw e def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](e) def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](e) + def foreach[U](f: T => U) {} def map[U](f: T => U): Try[U] = Failure[U](e) def filter(p: T => Boolean): Try[T] = this - def onFailure[U](rescueException: Throwable => U): Try[T] = { - rescueException(e) - this - } - def onSuccess[U](f: T => U): Try[T] = this def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = if (rescueException.isDefinedAt(e)) { Try(rescueException(e)) @@ -162,35 +141,33 @@ final case class Failure[+T](e: Throwable) extends Try[T] { } } + final case class Success[+T](r: T) extends Try[T] { def isFailure = false def isSuccess = true def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(r) - def apply() = r + def get = r def flatMap[U](f: T => Try[U]): Try[U] = try f(r) catch { case e => Failure(e) } def flatten[U](implicit ev: T <:< Try[U]): Try[U] = r + def foreach[U](f: T => U) = f(r) def map[U](f: T => U): Try[U] = Try[U](f(r)) def filter(p: T => Boolean): Try[T] = if (p(apply())) this else Failure(new NoSuchElementException("Predicate does not hold")) - def onFailure[U](rescueException: Throwable => U): Try[T] = this - def onSuccess[U](f: T => U): Try[T] = { - f(r) - this - } def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this } -object Try { +object Try { + def apply[T](r: => T): Try[T] = { try { Success(r) } catch { case e => Failure(e) } } - -} \ No newline at end of file + +} -- cgit v1.2.3 From b346ad485ff55091a370c93238628ea4fe1249b1 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 30 Jan 2012 16:11:30 +0100 Subject: Refactor the Try monad a bit. --- src/library/scala/util/Try.scala | 82 ++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index a810195b9f..b82570cd05 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -10,6 +10,10 @@ package scala.util +import collection.Seq + + + /** * The `Try` type represents a computation that may either result in an exception, * or return a success value. It's analagous to the `Either` type. @@ -33,7 +37,7 @@ sealed abstract class Try[+T] { /** * Returns the value from this `Success` or throws the exception if this is a `Failure`. */ - def get + def get: T /** * Applies the given function f if this is a Result. @@ -50,70 +54,50 @@ sealed abstract class Try[+T] { */ def map[U](f: T => U): Try[U] + def collect[U](pf: PartialFunction[T, U]): Try[U] + + def exists(p: T => Boolean): Boolean + /** * Converts this to a `Failure` if the predicate is not satisfied. */ def filter(p: T => Boolean): Try[T] /** - * Calls the exceptionHandler with the exception if this is a `Failure`. This is like `flatMap` for the exception. + * Converts this to a `Failure` if the predicate is not satisfied. */ - def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] + def filterNote(p: T => Boolean): Try[T] = filter(x => !p(x)) /** - * Calls the exceptionHandler with the exception if this is a `Failure`. This is like map for the exception. + * Calls the exceptionHandler with the exception if this is a `Failure`. This is like `flatMap` for the exception. */ - def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] + def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] /** - * Invoked regardless of whether the computation completed - * successfully or unsuccessfully. Implemented in terms of - * `respond` so that subclasses control evaluation order. Returns a - * chained `this` as in `respond`. + * Calls the exceptionHandler with the exception if this is a `Failure`. This is like map for the exception. */ - def ensure[U](f: => U): Try[T] = - respond { _ => f } - + def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] + /** * Returns `None` if this is a `Failure` or a `Some` containing the value if this is a `Success`. */ def toOption = if (isSuccess) Some(get) else None + def toSeq = if (isSuccess) Seq(get) else Seq() + /** - * Invokes the given closure when the value is available. Returns - * another 'This[R]' that is guaranteed to be available only *after* - * 'k' has run. This enables the enforcement of invocation ordering. - * - * This is overridden by subclasses. - */ - def respond[U](k: Try[T] => U): Try[T] = { - k(this) - this - } - - /** - * Invokes the given transformation when the value is available, - * returning the transformed value. This method is like a combination - * of `flatMap` and `rescue`. This method is typically used for more - * imperative control-flow than flatMap/rescue which often exploits - * the Null Object Pattern. - * - * This is overridden by subclasses. - */ - def transform[U](f: Try[T] => Try[U]): Try[U] = - f(this) - - /** - * Returns the given function applied to the value from this Success or returns this if this is a `Failure. + * Returns the given function applied to the value from this Success or returns this if this is a `Failure`. * Alias for `flatMap`. */ - def andThen[U](f: T => Try[U]) = flatMap(f) + def andThen[U](f: T => Try[U]): Try[U] = flatMap(f) /** * Transforms a nested `Try`, i.e., a `Try` of type `Try[Try[T]]`, - * into an un-nested `Try`, i.e., a `Try` of type `Try[T]` + * into an un-nested `Try`, i.e., a `Try` of type `Try[T]`. */ def flatten[U](implicit ev: T <:< Try[U]): Try[U] + + def failed: Try[Throwable] } @@ -130,15 +114,18 @@ final case class Failure[+T](e: Throwable) extends Try[T] { def get: T = throw e def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](e) def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](e) - def foreach[U](f: T => U) {} + def foreach[U](f: T => U): Unit = {} def map[U](f: T => U): Try[U] = Failure[U](e) + def collect[U](pf: PartialFunction[T, U]): Try[U] = Failure[U](e) def filter(p: T => Boolean): Try[T] = this - def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = + def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = if (rescueException.isDefinedAt(e)) { Try(rescueException(e)) } else { this } + def exists(p: T => Boolean): Boolean = false + def failed: Try[Throwable] = Success(e) } @@ -153,12 +140,17 @@ final case class Success[+T](r: T) extends Try[T] { case e => Failure(e) } def flatten[U](implicit ev: T <:< Try[U]): Try[U] = r - def foreach[U](f: T => U) = f(r) + def foreach[U](f: T => U): Unit = f(r) def map[U](f: T => U): Try[U] = Try[U](f(r)) + def collect[U](pf: PartialFunction[T, U]): Try[U] = + if (pf isDefinedAt r) Success(pf(r)) + else Failure[U](new NoSuchElementException("Partial function not defined at " + r)) def filter(p: T => Boolean): Try[T] = - if (p(apply())) this - else Failure(new NoSuchElementException("Predicate does not hold")) - def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this + if (p(r)) this + else Failure(new NoSuchElementException("Predicate does not hold for " + r)) + def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this + def exists(p: T => Boolean): Boolean = p(r) + def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed")) } -- cgit v1.2.3 From 8f36bf7a71f29af5ab61a3f58897881932c1daa3 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Mon, 30 Jan 2012 16:33:05 +0100 Subject: Add some missing methods, remove obsolete methods in futures. Remove `ensure`. Add `reportFailure` to execution contexts. Add `zip` to futures. --- .../scala/concurrent/ExecutionContext.scala | 2 + src/library/scala/concurrent/Future.scala | 47 ++++++++++++---------- .../concurrent/akka/ExecutionContextImpl.scala | 8 +++- src/library/scala/concurrent/akka/Promise.scala | 2 +- .../scala/concurrent/default/TaskImpl.scala | 2 + src/library/scala/concurrent/package.scala | 16 ++------ 6 files changed, 41 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 5ad9265f59..40fafd130c 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -37,6 +37,8 @@ trait ExecutionContext { def blocking[T](awaitable: Awaitable[T], atMost: Duration): T + def reportFailure(t: Throwable): Unit + /* implementations follow */ private implicit val executionContext = this diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 1b20c91e49..8d69586fbc 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -330,7 +330,7 @@ self => * future (6 / 0) rescue { case e: ArithmeticException => f } // result: Int.MaxValue * }}} */ - def rescue[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = { + def tryRecover[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = { val p = newPromise[U] onComplete { @@ -349,6 +349,31 @@ self => p.future } + /** Zips the values of `this` and `that` future, and creates + * a new future holding the tuple of their results. + * + * If `this` future fails, the resulting future is failed + * with the throwable stored in `this`. + * Otherwise, if `that` future fails, the resulting future is failed + * with the throwable stored in `that`. + */ + def zip[U](that: Future[U]): Future[(T, U)] = { + val p = newPromise[(T, U)] + + this onComplete { + case Left(t) => p failure t + case Right(r) => that onSuccess { + case r2 => p success ((r, r2)) + } + } + + that onFailure { + case f => p failure f + } + + p.future + } + /** Creates a new future which holds the result of this future if it was completed successfully, or, if not, * the result of the `that` future if `that` is completed successfully. * If both futures are failed, the resulting future holds the throwable object of the first future. @@ -412,26 +437,6 @@ self => p.future } - /** Executes a piece of code once this future is completed, regardless of whether - * or not the future fails or succeeds, and returns a new future with the result of this - * future. - * - * This method allows one to enforce ordering. - * - * The below example always executes the `println` calls in order: - * {{{ - * val f = future { 5 } - * f ensure { - * println("The value is available.") - * } ensure { - * println("The application can now end.") - * } - * }}} - */ - def ensure[U](body: =>U): Future[T] = andThen { - case _ => body - } - /** Creates a new future which holds the result of either this future or `that` future, depending on * which future was completed first. * diff --git a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala index 922d77189c..e2773e1fd5 100644 --- a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala @@ -22,7 +22,7 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo def execute(runnable: Runnable): Unit = executorService match { // case fj: ForkJoinPool => - // // TODO fork if more applicable + // TODO fork if more applicable // executorService execute runnable case _ => executorService execute runnable @@ -60,6 +60,10 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo } } + def reportFailure(t: Throwable) { + t.printStackTrace() + } + /** Only callable from the tasks running on the same execution context. */ private def blockingCall[T](body: Awaitable[T]): T = { releaseStack() @@ -104,7 +108,7 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo } catch { case e => // TODO catching all and continue isn't good for OOME - e.printStackTrace() + reportFailure(e) } } } finally { diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index 923d5baf6d..fad78478a5 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -199,7 +199,7 @@ object Promise { try { func(result) } catch { - case e => e.printStackTrace() + case e => executor.reportFailure(e) } } } diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 3e52d79894..7ac297de9e 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -290,6 +290,8 @@ private[concurrent] final class ExecutionContextImpl extends ExecutionContext { res } + def reportFailure(t: Throwable): Unit = {} + } diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 6c1e323595..5c3f1f818d 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -121,11 +121,6 @@ package object concurrent { object nondeterministic { } - final class DurationOps private[concurrent] (x: Int) { - // TODO ADD OTHERS - def ns = util.Duration.fromNanos(0) - } - @inline implicit final def int2durationops(x: Int) = new DurationOps(x) } @@ -144,13 +139,10 @@ package concurrent { def this(origin: Future[_]) = this(origin, "Future timed out.") } - /** Evidence that the program can be nondeterministic. - * - * Programs in which such an evidence is available in scope - * can contain calls to methods which yield nondeterministic - * programs. - */ - sealed trait NonDeterministic + final class DurationOps private[concurrent] (x: Int) { + // TODO ADD OTHERS + def ns = util.Duration.fromNanos(0) + } } -- cgit v1.2.3 From 0d23e83d136436109c957fb44ac328bdfaa8a658 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Tue, 31 Jan 2012 13:07:13 +0100 Subject: Replaced Either with Try throughout scala.concurrent. --- .../scala/concurrent/ExecutionContext.scala | 11 ++-- src/library/scala/concurrent/Future.scala | 72 +++++++++++----------- src/library/scala/concurrent/Promise.scala | 10 +-- .../concurrent/akka/ExecutionContextImpl.scala | 4 +- src/library/scala/concurrent/akka/Future.scala | 13 ++-- src/library/scala/concurrent/akka/Promise.scala | 46 +++++++------- .../scala/concurrent/default/TaskImpl.scala | 32 +++++----- src/library/scala/concurrent/package.scala | 27 ++++---- src/library/scala/util/Try.scala | 22 +++---- 9 files changed, 122 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 40fafd130c..99cd264ac5 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -13,6 +13,7 @@ package scala.concurrent import java.util.concurrent.atomic.{ AtomicInteger } import java.util.concurrent.{ Executors, Future => JFuture, Callable } import scala.util.Duration +import scala.util.{ Try, Success, Failure } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } import scala.collection.generic.CanBuildFrom import collection._ @@ -74,9 +75,9 @@ trait ExecutionContext { buffer += null.asInstanceOf[T] counter.incrementAndGet() f onComplete { - case Left(t) => + case Failure(t) => p tryFailure t - case Right(v) => + case Success(v) => buffer(currentIndex) = v tryFinish() } @@ -93,7 +94,7 @@ trait ExecutionContext { */ def any[T](futures: Traversable[Future[T]]): Future[T] = { val p = promise[T] - val completeFirst: Either[Throwable, T] => Unit = elem => p tryComplete elem + val completeFirst: Try[T] => Unit = elem => p tryComplete elem futures foreach (_ onComplete completeFirst) @@ -108,9 +109,9 @@ trait ExecutionContext { else { val result = promise[Option[T]] val count = new AtomicInteger(futures.size) - val search: Either[Throwable, T] => Unit = { + val search: Try[T] => Unit = { v => v match { - case Right(r) => if (predicate(r)) result trySuccess Some(r) + case Success(r) => if (predicate(r)) result trySuccess Some(r) case _ => } if (count.decrementAndGet() == 0) result trySuccess None diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 8d69586fbc..c87c51c0df 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -17,7 +17,7 @@ import java.util.{ LinkedList => JLinkedList } import java.{ lang => jl } import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } -import scala.util.{ Timeout, Duration } +import scala.util.{ Timeout, Duration, Try, Success, Failure } import scala.Option import scala.annotation.tailrec @@ -96,8 +96,8 @@ self => * $multipleCallbacks */ def onSuccess[U](pf: PartialFunction[T, U]): this.type = onComplete { - case Left(t) => // do nothing - case Right(v) if pf isDefinedAt v => pf(v) + case Failure(t) => // do nothing + case Success(v) => if (pf isDefinedAt v) pf(v) else { /*do nothing*/ } } /** When this future is completed with a failure (i.e. with a throwable), @@ -113,8 +113,8 @@ self => * $multipleCallbacks */ def onFailure[U](callback: PartialFunction[Throwable, U]): this.type = onComplete { - case Left(t) if isFutureThrowable(t) => if (callback.isDefinedAt(t)) callback(t) - case Right(v) => // do nothing + case Failure(t) => if (isFutureThrowable(t) && callback.isDefinedAt(t)) callback(t) else { /*do nothing*/ } + case Success(v) => // do nothing } /** When this future is completed, either through an exception, a timeout, or a value, @@ -125,7 +125,7 @@ self => * * $multipleCallbacks */ - def onComplete[U](func: Either[Throwable, T] => U): this.type + def onComplete[U](func: Try[T] => U): this.type /* Miscellaneous */ @@ -156,8 +156,8 @@ self => val p = newPromise[Throwable] onComplete { - case Left(t) => p success t - case Right(v) => p failure noSuchElem(v) + case Failure(t) => p success t + case Success(v) => p failure noSuchElem(v) } p.future @@ -171,8 +171,8 @@ self => * Will not be called if the future fails. */ def foreach[U](f: T => U): Unit = onComplete { - case Right(r) => f(r) - case Left(_) => // do nothing + case Success(r) => f(r) + case Failure(_) => // do nothing } /** Creates a new future by applying a function to the successful result of @@ -185,8 +185,8 @@ self => val p = newPromise[S] onComplete { - case Left(t) => p failure t - case Right(v) => + case Failure(t) => p failure t + case Success(v) => try p success f(v) catch { case t => p complete resolver(t) @@ -207,12 +207,12 @@ self => val p = newPromise[S] onComplete { - case Left(t) => p failure t - case Right(v) => + case Failure(t) => p failure t + case Success(v) => try { f(v) onComplete { - case Left(t) => p failure t - case Right(v) => p success v + case Failure(t) => p failure t + case Success(v) => p success v } } catch { case t: Throwable => p complete resolver(t) @@ -242,8 +242,8 @@ self => val p = newPromise[T] onComplete { - case Left(t) => p failure t - case Right(v) => + case Failure(t) => p failure t + case Success(v) => try { if (pred(v)) p success v else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) @@ -279,8 +279,8 @@ self => val p = newPromise[S] onComplete { - case Left(t) => p failure t - case Right(v) => + case Failure(t) => p failure t + case Success(v) => try { if (pf.isDefinedAt(v)) p success pf(v) else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) @@ -308,7 +308,7 @@ self => val p = newPromise[U] onComplete { - case Left(t) if pf isDefinedAt t => + case Failure(t) if pf isDefinedAt t => try { p success pf(t) } catch { case t: Throwable => p complete resolver(t) } case otherwise => p complete otherwise @@ -334,11 +334,11 @@ self => val p = newPromise[U] onComplete { - case Left(t) if pf isDefinedAt t => + case Failure(t) if pf isDefinedAt t => try { pf(t) onComplete { - case Left(t) => p failure t - case Right(v) => p success v + case Failure(t) => p failure t + case Success(v) => p success v } } catch { case t: Throwable => p complete resolver(t) @@ -361,8 +361,8 @@ self => val p = newPromise[(T, U)] this onComplete { - case Left(t) => p failure t - case Right(r) => that onSuccess { + case Failure(t) => p failure t + case Success(r) => that onSuccess { case r2 => p success ((r, r2)) } } @@ -392,11 +392,11 @@ self => val p = newPromise[U] onComplete { - case Left(t) => that onComplete { - case Left(_) => p failure t - case Right(v) => p success v + case Failure(t) => that onComplete { + case Failure(_) => p failure t + case Success(v) => p success v } - case Right(v) => p success v + case Success(v) => p success v } p.future @@ -420,12 +420,12 @@ self => * f andThen { * case r => sys.error("runtime exception") * } andThen { - * case Left(t) => println(t) - * case Right(v) => println(v) + * case Failure(t) => println(t) + * case Success(v) => println(v) * } * }}} */ - def andThen[U](pf: PartialFunction[Either[Throwable, T], U]): Future[T] = { + def andThen[U](pf: PartialFunction[Try[T], U]): Future[T] = { val p = newPromise[T] onComplete { @@ -453,9 +453,9 @@ self => def either[U >: T](that: Future[U]): Future[U] = { val p = self.newPromise[U] - val completePromise: PartialFunction[Either[Throwable, U], _] = { - case Left(t) => p tryFailure t - case Right(v) => p trySuccess v + val completePromise: PartialFunction[Try[U], _] = { + case Failure(t) => p tryFailure t + case Success(v) => p trySuccess v } self onComplete completePromise diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index acc02fba5f..f26deb77ab 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -8,7 +8,7 @@ package scala.concurrent - +import scala.util.{ Try, Success, Failure } @@ -42,7 +42,7 @@ trait Promise[T] { * * $promiseCompletion */ - def complete(result: Either[Throwable, T]): this.type = if (tryComplete(result)) this else throwCompleted + def complete(result:Try[T]): this.type = if (tryComplete(result)) this else throwCompleted /** Tries to complete the promise with either a value or the exception. * @@ -50,7 +50,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryComplete(result: Either[Throwable, T]): Boolean + def tryComplete(result: Try[T]): Boolean /** Completes this promise with the specified future, once that future is completed. * @@ -77,7 +77,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def trySuccess(value: T): Boolean = tryComplete(Right(value)) + def trySuccess(value: T): Boolean = tryComplete(Success(value)) /** Completes the promise with an exception. * @@ -95,7 +95,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryFailure(t: Throwable): Boolean = tryComplete(Left(t)) + def tryFailure(t: Throwable): Boolean = tryComplete(Failure(t)) /** Wraps a `Throwable` in an `ExecutionException` if necessary. TODO replace with `resolver` from scala.concurrent * diff --git a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala index e2773e1fd5..f8c99d857b 100644 --- a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala @@ -12,7 +12,7 @@ package scala.concurrent.akka import java.util.concurrent.{Callable, ExecutorService} import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} -import scala.util.Duration +import scala.util.{ Duration, Try, Success, Failure } import scala.collection.mutable.Stack @@ -41,7 +41,7 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo () => p complete { try { - Right(body) + Success(body) } catch { case e => resolver(e) } diff --git a/src/library/scala/concurrent/akka/Future.scala b/src/library/scala/concurrent/akka/Future.scala index be1a9ec2ae..2633e751bd 100644 --- a/src/library/scala/concurrent/akka/Future.scala +++ b/src/library/scala/concurrent/akka/Future.scala @@ -11,6 +11,7 @@ package scala.concurrent.akka import scala.concurrent.{Awaitable, ExecutionContext} +import scala.util.{ Try, Success, Failure } //import scala.util.continuations._ @@ -35,9 +36,9 @@ trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { * if it contains a valid result, or Some(Left(error)) if it contains * an exception. */ - def value: Option[Either[Throwable, T]] + def value: Option[Try[T]] - def onComplete[U](func: Either[Throwable, T] => U): this.type + def onComplete[U](func: Try[T] => U): this.type /** Creates a new Future[A] which is completed with this Future's result if * that conforms to A's erased type or a ClassCastException otherwise. @@ -46,12 +47,12 @@ trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { val p = executor.promise[T] onComplete { - case l @ Left(t) => p complete l.asInstanceOf[Either[Throwable, T]] - case Right(v) => + case f @ Failure(t) => p complete f.asInstanceOf[Try[T]] + case Success(v) => p complete (try { - Right(boxedType(m.erasure).cast(v).asInstanceOf[T]) + Success(boxedType(m.erasure).cast(v).asInstanceOf[T]) } catch { - case e: ClassCastException ⇒ Left(e) + case e: ClassCastException ⇒ Failure(e) }) } diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala index fad78478a5..8ecffec2aa 100644 --- a/src/library/scala/concurrent/akka/Promise.scala +++ b/src/library/scala/concurrent/akka/Promise.scala @@ -15,8 +15,10 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} //import scala.util.continuations._ import scala.util.Duration +import scala.util.Try +import scala.util import scala.annotation.tailrec -import scala.concurrent.NonDeterministic +//import scala.concurrent.NonDeterministic trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { @@ -77,18 +79,18 @@ object Promise { /** Represents the internal state. */ - sealed trait FState[+T] { def value: Option[Either[Throwable, T]] } + sealed trait FState[+T] { def value: Option[Try[T]] } - case class Pending[T](listeners: List[Either[Throwable, T] => Any] = Nil) extends FState[T] { - def value: Option[Either[Throwable, T]] = None + case class Pending[T](listeners: List[Try[T] => Any] = Nil) extends FState[T] { + def value: Option[Try[T]] = None } - case class Success[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { - def result: T = value.get.right.get + case class Success[T](value: Option[util.Success[T]] = None) extends FState[T] { + def result: T = value.get.get } - case class Failure[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { - def exception: Throwable = value.get.left.get + case class Failure[T](value: Option[util.Failure[T]] = None) extends FState[T] { + def exception: Throwable = value.get.exception } private val emptyPendingValue = Pending[Nothing](Nil) @@ -129,11 +131,11 @@ object Promise { def await(atMost: Duration)(implicit permit: CanAwait): T = ready(atMost).value.get match { - case Left(e) => throw e - case Right(r) => r + case util.Failure(e) => throw e + case util.Success(r) => r } - def value: Option[Either[Throwable, T]] = getState.value + def value: Option[Try[T]] = getState.value @inline private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]] @@ -144,14 +146,14 @@ object Promise { @inline protected final def getState: FState[T] = updater.get(this) - def tryComplete(value: Either[Throwable, T]): Boolean = { - val callbacks: List[Either[Throwable, T] => Any] = { + def tryComplete(value: Try[T]): Boolean = { + val callbacks: List[Try[T] => Any] = { try { @tailrec - def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Any] = { + def tryComplete(v: Try[T]): List[Try[T] => Any] = { getState match { case cur @ Pending(listeners) => - if (updateState(cur, if (v.isLeft) Failure(Some(v)) else Success(Some(v)))) listeners + if (updateState(cur, if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) else Success(Some(v.asInstanceOf[util.Success[T]])))) listeners else tryComplete(v) case _ => null } @@ -173,7 +175,7 @@ object Promise { } } - def onComplete[U](func: Either[Throwable, T] => U): this.type = { + def onComplete[U](func: Try[T] => U): this.type = { @tailrec // Returns whether the future has already been completed or not def tryAddCallback(): Boolean = { val cur = getState @@ -195,7 +197,7 @@ object Promise { this } - private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T]) { + private final def notifyCompleted(func: Try[T] => Any, result: Try[T]) { try { func(result) } catch { @@ -208,12 +210,12 @@ object Promise { * * Useful in Future-composition when a value to contribute is already available. */ - final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { + final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { val value = Some(resolve(suppliedValue)) - def tryComplete(value: Either[Throwable, T]): Boolean = false + def tryComplete(value: Try[T]): Boolean = false - def onComplete[U](func: Either[Throwable, T] => U): this.type = { + def onComplete[U](func: Try[T] => U): this.type = { val completedAs = value.get executor dispatchFuture { () => func(completedAs) @@ -224,8 +226,8 @@ object Promise { private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this def await(atMost: Duration)(implicit permit: CanAwait): T = value.get match { - case Left(e) => throw e - case Right(r) => r + case util.Failure(e) => throw e + case util.Success(r) => r } } diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala index 7ac297de9e..94e54cb372 100644 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ b/src/library/scala/concurrent/default/TaskImpl.scala @@ -5,6 +5,8 @@ package default import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } +import scala.util.Try +import scala.util import scala.util.Duration import scala.annotation.tailrec @@ -17,7 +19,7 @@ self: Future[T] => def newPromise[S]: Promise[S] = executor promise - type Callback = Either[Throwable, T] => Any + type Callback = Try[T] => Any def getState: State[T] @@ -25,22 +27,22 @@ self: Future[T] => protected def dispatch[U](r: Runnable) = executionContext execute r - protected def processCallbacks(cbs: List[Callback], r: Either[Throwable, T]) = + protected def processCallbacks(cbs: List[Callback], r: Try[T]) = for (cb <- cbs) dispatch(new Runnable { override def run() = cb(r) }) def future: Future[T] = self - def onComplete[U](callback: Either[Throwable, T] => U): this.type = { - @tailrec def tryAddCallback(): Either[Throwable, T] = { + def onComplete[U](callback: Try[T] => U): this.type = { + @tailrec def tryAddCallback(): Try[T] = { getState match { case p @ Pending(lst) => val pt = p.asInstanceOf[Pending[T]] if (casState(pt, Pending(callback :: pt.callbacks))) null else tryAddCallback() - case Success(res) => Right(res) - case Failure(t) => Left(t) + case Success(res) => util.Success(res) + case Failure(t) => util.Failure(t) } } @@ -87,9 +89,9 @@ extends Promise[T] with Future[T] with Completable[T] { case _ => null } - def tryComplete(r: Either[Throwable, T]) = r match { - case Left(t) => tryFailure(t) - case Right(v) => trySuccess(v) + def tryComplete(r: Try[T]) = r match { + case util.Failure(t) => tryFailure(t) + case util.Success(v) => trySuccess(v) } override def trySuccess(value: T): Boolean = { @@ -97,7 +99,7 @@ extends Promise[T] with Future[T] with Completable[T] { if (cbs == null) false else { - processCallbacks(cbs, Right(value)) + processCallbacks(cbs, util.Success(value)) this.synchronized { this.notifyAll() } @@ -111,7 +113,7 @@ extends Promise[T] with Future[T] with Completable[T] { if (cbs == null) false else { - processCallbacks(cbs, Left(wrapped)) + processCallbacks(cbs, util.Failure(wrapped)) this.synchronized { this.notifyAll() } @@ -163,13 +165,13 @@ extends RecursiveAction with Task[T] with Future[T] with Completable[T] { var cbs: List[Callback] = null try { val res = body - processCallbacks(tryCompleteState(Success(res)), Right(res)) + processCallbacks(tryCompleteState(Success(res)), util.Success(res)) } catch { case t if isFutureThrowable(t) => - processCallbacks(tryCompleteState(Failure(t)), Left(t)) + processCallbacks(tryCompleteState(Failure(t)), util.Failure(t)) case t => val ee = new ExecutionException(t) - processCallbacks(tryCompleteState(Failure(ee)), Left(ee)) + processCallbacks(tryCompleteState(Failure(ee)), util.Failure(ee)) throw t } } @@ -207,7 +209,7 @@ extends RecursiveAction with Task[T] with Future[T] with Completable[T] { private[concurrent] sealed abstract class State[T] -case class Pending[T](callbacks: List[Either[Throwable, T] => Any]) extends State[T] +case class Pending[T](callbacks: List[Try[T] => Any]) extends State[T] case class Success[T](result: T) extends State[T] diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 5c3f1f818d..35b8cf6664 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -10,7 +10,7 @@ package scala -import scala.util.Duration +import scala.util.{ Duration, Try, Success, Failure } @@ -44,23 +44,24 @@ package object concurrent { case _ => true } - private[concurrent] def resolve[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { - case Left(t: scala.runtime.NonLocalReturnControl[_]) => Right(t.value.asInstanceOf[T]) - case Left(t: scala.util.control.ControlThrowable) => Left(new ExecutionException("Boxed ControlThrowable", t)) - case Left(t: InterruptedException) => Left(new ExecutionException("Boxed InterruptedException", t)) - case Left(e: Error) => Left(new ExecutionException("Boxed Error", e)) + private[concurrent] def resolve[T](source: Try[T]): Try[T] = source match { + case Failure(t: scala.runtime.NonLocalReturnControl[_]) => Success(t.value.asInstanceOf[T]) + case Failure(t: scala.util.control.ControlThrowable) => Failure(new ExecutionException("Boxed ControlThrowable", t)) + case Failure(t: InterruptedException) => Failure(new ExecutionException("Boxed InterruptedException", t)) + case Failure(e: Error) => Failure(new ExecutionException("Boxed Error", e)) case _ => source } - private val resolverFunction: PartialFunction[Throwable, Either[Throwable, _]] = { - case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value) - case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) - case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) - case e: Error => Left(new ExecutionException("Boxed Error", e)) - case t => Left(t) + // TODO, docs, return type + private val resolverFunction: PartialFunction[Throwable, Try[_]] = { + case t: scala.runtime.NonLocalReturnControl[_] => Success(t.value) + case t: scala.util.control.ControlThrowable => Failure(new ExecutionException("Boxed ControlThrowable", t)) + case t: InterruptedException => Failure(new ExecutionException("Boxed InterruptedException", t)) + case e: Error => Failure(new ExecutionException("Boxed Error", e)) + case t => Failure(t) } - private[concurrent] def resolver[T] = resolverFunction.asInstanceOf[PartialFunction[Throwable, Either[Throwable, T]]] + private[concurrent] def resolver[T] = resolverFunction.asInstanceOf[PartialFunction[Throwable, Try[T]]] /* concurrency constructs */ diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index b82570cd05..a05a75e0b7 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -66,7 +66,7 @@ sealed abstract class Try[+T] { /** * Converts this to a `Failure` if the predicate is not satisfied. */ - def filterNote(p: T => Boolean): Try[T] = filter(x => !p(x)) + def filterNot(p: T => Boolean): Try[T] = filter(x => !p(x)) /** * Calls the exceptionHandler with the exception if this is a `Failure`. This is like `flatMap` for the exception. @@ -101,31 +101,31 @@ sealed abstract class Try[+T] { } -final case class Failure[+T](e: Throwable) extends Try[T] { +final case class Failure[+T](val exception: Throwable) extends Try[T] { def isFailure = true def isSuccess = false def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = { try { - if (rescueException.isDefinedAt(e)) rescueException(e) else this + if (rescueException.isDefinedAt(exception)) rescueException(exception) else this } catch { case e2 => Failure(e2) } } - def get: T = throw e - def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](e) - def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](e) + def get: T = throw exception + def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](exception) + def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](exception) def foreach[U](f: T => U): Unit = {} - def map[U](f: T => U): Try[U] = Failure[U](e) - def collect[U](pf: PartialFunction[T, U]): Try[U] = Failure[U](e) + def map[U](f: T => U): Try[U] = Failure[U](exception) + def collect[U](pf: PartialFunction[T, U]): Try[U] = Failure[U](exception) def filter(p: T => Boolean): Try[T] = this def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = - if (rescueException.isDefinedAt(e)) { - Try(rescueException(e)) + if (rescueException.isDefinedAt(exception)) { + Try(rescueException(exception)) } else { this } def exists(p: T => Boolean): Boolean = false - def failed: Try[Throwable] = Success(e) + def failed: Try[Throwable] = Success(exception) } -- cgit v1.2.3 From 14bf00cda4922b778e2df72299a2a4e8fb2fd42b Mon Sep 17 00:00:00 2001 From: aleksandar Date: Wed, 1 Feb 2012 13:16:06 +0100 Subject: Rename to recoverWith and fallbackTo in Future. --- src/library/scala/concurrent/Future.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index c87c51c0df..3650e44261 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -330,7 +330,7 @@ self => * future (6 / 0) rescue { case e: ArithmeticException => f } // result: Int.MaxValue * }}} */ - def tryRecover[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = { + def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = { val p = newPromise[U] onComplete { @@ -388,7 +388,7 @@ self => * await(0) h // evaluates to 5 * }}} */ - def orElse[U >: T](that: Future[U]): Future[U] = { + def fallbackTo[U >: T](that: Future[U]): Future[U] = { val p = newPromise[U] onComplete { -- cgit v1.2.3 From ce0bfa62aa18f2b153aa5690ed34bf05f71bb7eb Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Fri, 3 Feb 2012 10:07:07 +0100 Subject: Fix typo in Future trait. Simplify recoverWith. --- src/library/scala/concurrent/Future.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 3650e44261..73f76bbbfb 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -327,7 +327,7 @@ self => * * {{{ * val f = future { Int.MaxValue } - * future (6 / 0) rescue { case e: ArithmeticException => f } // result: Int.MaxValue + * future (6 / 0) recoverWith { case e: ArithmeticException => f } // result: Int.MaxValue * }}} */ def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = { @@ -336,10 +336,7 @@ self => onComplete { case Failure(t) if pf isDefinedAt t => try { - pf(t) onComplete { - case Failure(t) => p failure t - case Success(v) => p success v - } + p completeWith pf(t) } catch { case t: Throwable => p complete resolver(t) } -- cgit v1.2.3 From f2ccb43a844e484ae511c8cff3fbf534a0d86ebe Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Fri, 3 Feb 2012 18:18:24 +0100 Subject: Change the default reportFailure implementation. --- src/library/scala/concurrent/akka/ExecutionContextImpl.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala index f8c99d857b..2bc846ba3c 100644 --- a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala @@ -60,8 +60,9 @@ class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionCo } } - def reportFailure(t: Throwable) { - t.printStackTrace() + def reportFailure(t: Throwable) = t match { + case e: Error => throw e // rethrow serious errors + case t => t.printStackTrace() } /** Only callable from the tasks running on the same execution context. */ -- cgit v1.2.3 From ab84c8d9a97b41728e77f7808eda2748d052ca06 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 16 Feb 2012 13:47:59 +0100 Subject: Disable execution context and futures implementation in the default package. Fixed some tests so that they work now. The Transactions.scala test was failing when defined in scala.concurrent package, reporting that type `_$1` is defined twice. Until we figure out the reason for this, the package name in that test is renamed. --- .../scala/concurrent/akka/AbstractPromise.java | 21 -- .../concurrent/akka/ExecutionContextImpl.scala | 134 ------- src/library/scala/concurrent/akka/Future.scala | 77 ---- src/library/scala/concurrent/akka/Promise.scala | 250 ------------- src/library/scala/concurrent/akka/package.scala | 39 -- .../scala/concurrent/default/SchedulerImpl.scala | 44 --- .../default/SchedulerImpl.scala.disabled | 44 +++ .../scala/concurrent/default/TaskImpl.scala | 313 ----------------- .../concurrent/default/TaskImpl.scala.disabled | 313 +++++++++++++++++ .../scala/concurrent/impl/AbstractPromise.java | 21 ++ .../concurrent/impl/ExecutionContextImpl.scala | 134 +++++++ src/library/scala/concurrent/impl/Future.scala | 77 ++++ src/library/scala/concurrent/impl/Promise.scala | 251 +++++++++++++ src/library/scala/concurrent/impl/package.scala | 39 ++ src/library/scala/concurrent/package.scala | 6 +- test/disabled/jvm/scala-concurrent-tck-akka.scala | 391 +++++++++++++++++++++ test/files/jvm/concurrent-future.scala | 14 +- test/files/jvm/scala-concurrent-tck-akka.scala | 391 --------------------- test/files/jvm/scala-concurrent-tck.scala | 23 +- test/files/pos/Transactions.scala | 2 +- 20 files changed, 1293 insertions(+), 1291 deletions(-) delete mode 100644 src/library/scala/concurrent/akka/AbstractPromise.java delete mode 100644 src/library/scala/concurrent/akka/ExecutionContextImpl.scala delete mode 100644 src/library/scala/concurrent/akka/Future.scala delete mode 100644 src/library/scala/concurrent/akka/Promise.scala delete mode 100644 src/library/scala/concurrent/akka/package.scala delete mode 100644 src/library/scala/concurrent/default/SchedulerImpl.scala create mode 100644 src/library/scala/concurrent/default/SchedulerImpl.scala.disabled delete mode 100644 src/library/scala/concurrent/default/TaskImpl.scala create mode 100644 src/library/scala/concurrent/default/TaskImpl.scala.disabled create mode 100644 src/library/scala/concurrent/impl/AbstractPromise.java create mode 100644 src/library/scala/concurrent/impl/ExecutionContextImpl.scala create mode 100644 src/library/scala/concurrent/impl/Future.scala create mode 100644 src/library/scala/concurrent/impl/Promise.scala create mode 100644 src/library/scala/concurrent/impl/package.scala create mode 100644 test/disabled/jvm/scala-concurrent-tck-akka.scala delete mode 100644 test/files/jvm/scala-concurrent-tck-akka.scala (limited to 'src') diff --git a/src/library/scala/concurrent/akka/AbstractPromise.java b/src/library/scala/concurrent/akka/AbstractPromise.java deleted file mode 100644 index 38c74edf2f..0000000000 --- a/src/library/scala/concurrent/akka/AbstractPromise.java +++ /dev/null @@ -1,21 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent.akka; - - - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; - - - -abstract class AbstractPromise { - private volatile Object _ref = null; - protected final static AtomicReferenceFieldUpdater updater = - AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); -} diff --git a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala b/src/library/scala/concurrent/akka/ExecutionContextImpl.scala deleted file mode 100644 index 2bc846ba3c..0000000000 --- a/src/library/scala/concurrent/akka/ExecutionContextImpl.scala +++ /dev/null @@ -1,134 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent.akka - - - -import java.util.concurrent.{Callable, ExecutorService} -import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} -import scala.util.{ Duration, Try, Success, Failure } -import scala.collection.mutable.Stack - - - -class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionContext { - import ExecutionContextImpl._ - - def execute(runnable: Runnable): Unit = executorService match { - // case fj: ForkJoinPool => - // TODO fork if more applicable - // executorService execute runnable - case _ => - executorService execute runnable - } - - def execute[U](body: () => U): Unit = execute(new Runnable { - def run() = body() - }) - - def promise[T]: Promise[T] = new Promise.DefaultPromise[T]()(this) - - def future[T](body: =>T): Future[T] = { - val p = promise[T] - - dispatchFuture { - () => - p complete { - try { - Success(body) - } catch { - case e => resolver(e) - } - } - } - - p.future - } - - def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { - currentExecutionContext.get match { - case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case - case x => x.blockingCall(awaitable) // inside an execution context thread - } - } - - def reportFailure(t: Throwable) = t match { - case e: Error => throw e // rethrow serious errors - case t => t.printStackTrace() - } - - /** Only callable from the tasks running on the same execution context. */ - private def blockingCall[T](body: Awaitable[T]): T = { - releaseStack() - - // TODO see what to do with timeout - body.await(Duration.fromNanos(0))(CanAwaitEvidence) - } - - // an optimization for batching futures - // TODO we should replace this with a public queue, - // so that it can be stolen from - // OR: a push to the local task queue should be so cheap that this is - // not even needed, but stealing is still possible - private val _taskStack = new ThreadLocal[Stack[() => Unit]]() - - private def releaseStack(): Unit = - _taskStack.get match { - case stack if (stack ne null) && stack.nonEmpty => - val tasks = stack.elems - stack.clear() - _taskStack.remove() - dispatchFuture(() => _taskStack.get.elems = tasks, true) - case null => - // do nothing - there is no local batching stack anymore - case _ => - _taskStack.remove() - } - - private[akka] def dispatchFuture(task: () => Unit, force: Boolean = false): Unit = - _taskStack.get match { - case stack if (stack ne null) && !force => stack push task - case _ => this.execute( - new Runnable { - def run() { - try { - val taskStack = Stack[() => Unit](task) - _taskStack set taskStack - while (taskStack.nonEmpty) { - val next = taskStack.pop() - try { - next.apply() - } catch { - case e => - // TODO catching all and continue isn't good for OOME - reportFailure(e) - } - } - } finally { - _taskStack.remove() - } - } - } - ) - } - -} - - -object ExecutionContextImpl { - - private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContextImpl] = new ThreadLocal[ExecutionContextImpl] { - override protected def initialValue = null - } - -} - - diff --git a/src/library/scala/concurrent/akka/Future.scala b/src/library/scala/concurrent/akka/Future.scala deleted file mode 100644 index 2633e751bd..0000000000 --- a/src/library/scala/concurrent/akka/Future.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent.akka - - - -import scala.concurrent.{Awaitable, ExecutionContext} -import scala.util.{ Try, Success, Failure } -//import scala.util.continuations._ - - - -trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { - - implicit def executor: ExecutionContextImpl - - /** For use only within a Future.flow block or another compatible Delimited Continuations reset block. - * - * Returns the result of this Future without blocking, by suspending execution and storing it as a - * continuation until the result is available. - */ - //def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T => Future[Any])) - - /** Tests whether this Future has been completed. - */ - final def isCompleted: Boolean = value.isDefined - - /** The contained value of this Future. Before this Future is completed - * the value will be None. After completion the value will be Some(Right(t)) - * if it contains a valid result, or Some(Left(error)) if it contains - * an exception. - */ - def value: Option[Try[T]] - - def onComplete[U](func: Try[T] => U): this.type - - /** Creates a new Future[A] which is completed with this Future's result if - * that conforms to A's erased type or a ClassCastException otherwise. - */ - final def mapTo[T](implicit m: Manifest[T]) = { - val p = executor.promise[T] - - onComplete { - case f @ Failure(t) => p complete f.asInstanceOf[Try[T]] - case Success(v) => - p complete (try { - Success(boxedType(m.erasure).cast(v).asInstanceOf[T]) - } catch { - case e: ClassCastException ⇒ Failure(e) - }) - } - - p.future - } - - /** Used by for-comprehensions. - */ - final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p) - - final class FutureWithFilter[+A](self: Future[A], p: A => Boolean) { - def foreach(f: A => Unit): Unit = self filter p foreach f - def map[B](f: A => B) = self filter p map f - def flatMap[B](f: A => Future[B]) = self filter p flatMap f - def withFilter(q: A => Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) - } - -} - - - - diff --git a/src/library/scala/concurrent/akka/Promise.scala b/src/library/scala/concurrent/akka/Promise.scala deleted file mode 100644 index 8ecffec2aa..0000000000 --- a/src/library/scala/concurrent/akka/Promise.scala +++ /dev/null @@ -1,250 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent.akka - - - -import java.util.concurrent.TimeUnit.{ NANOSECONDS, MILLISECONDS } -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater -import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} -//import scala.util.continuations._ -import scala.util.Duration -import scala.util.Try -import scala.util -import scala.annotation.tailrec -//import scala.concurrent.NonDeterministic - - -trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { - - def future = this - - def newPromise[S]: Promise[S] = executor promise - - // TODO refine answer and return types here from Any to type parameters - // then move this up in the hierarchy - /* - final def <<(value: T): Future[T] @cps[Future[Any]] = shift { - cont: (Future[T] => Future[Any]) => - cont(complete(Right(value))) - } - - final def <<(other: Future[T]): Future[T] @cps[Future[Any]] = shift { - cont: (Future[T] => Future[Any]) => - val p = executor.promise[Any] - val thisPromise = this - - thisPromise completeWith other - thisPromise onComplete { v => - try { - p completeWith cont(thisPromise) - } catch { - case e => p complete resolver(e) - } - } - - p.future - } - */ - // TODO finish this once we introduce something like dataflow streams - - /* - final def <<(stream: PromiseStreamOut[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => - val fr = executor.promise[Any] - val f = stream.dequeue(this) - f.onComplete { _ => - try { - fr completeWith cont(f) - } catch { - case e => - fr failure e - } - } - fr - } - */ - -} - - -object Promise { - - def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] - - /** Represents the internal state. - */ - sealed trait FState[+T] { def value: Option[Try[T]] } - - case class Pending[T](listeners: List[Try[T] => Any] = Nil) extends FState[T] { - def value: Option[Try[T]] = None - } - - case class Success[T](value: Option[util.Success[T]] = None) extends FState[T] { - def result: T = value.get.get - } - - case class Failure[T](value: Option[util.Failure[T]] = None) extends FState[T] { - def exception: Throwable = value.get.exception - } - - private val emptyPendingValue = Pending[Nothing](Nil) - - /** Default promise implementation. - */ - class DefaultPromise[T](implicit val executor: ExecutionContextImpl) extends AbstractPromise with Promise[T] { - self => - - updater.set(this, Promise.EmptyPending()) - - protected final def tryAwait(atMost: Duration): Boolean = { - @tailrec - def awaitUnsafe(waitTimeNanos: Long): Boolean = { - if (value.isEmpty && waitTimeNanos > 0) { - val ms = NANOSECONDS.toMillis(waitTimeNanos) - val ns = (waitTimeNanos % 1000000l).toInt // as per object.wait spec - val start = System.nanoTime() - try { - synchronized { - while (value.isEmpty) wait(ms, ns) - } - } catch { - case e: InterruptedException => - } - - awaitUnsafe(waitTimeNanos - (System.nanoTime() - start)) - } else - value.isDefined - } - - executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), Duration.fromNanos(0)) - } - - private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = - if (value.isDefined || tryAwait(atMost)) this - else throw new TimeoutException("Futures timed out after [" + atMost.toMillis + "] milliseconds") - - def await(atMost: Duration)(implicit permit: CanAwait): T = - ready(atMost).value.get match { - case util.Failure(e) => throw e - case util.Success(r) => r - } - - def value: Option[Try[T]] = getState.value - - @inline - private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]] - - @inline - protected final def updateState(oldState: FState[T], newState: FState[T]): Boolean = updater.compareAndSet(this, oldState, newState) - - @inline - protected final def getState: FState[T] = updater.get(this) - - def tryComplete(value: Try[T]): Boolean = { - val callbacks: List[Try[T] => Any] = { - try { - @tailrec - def tryComplete(v: Try[T]): List[Try[T] => Any] = { - getState match { - case cur @ Pending(listeners) => - if (updateState(cur, if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) else Success(Some(v.asInstanceOf[util.Success[T]])))) listeners - else tryComplete(v) - case _ => null - } - } - tryComplete(resolve(value)) - } finally { - synchronized { notifyAll() } // notify any blockers from `tryAwait` - } - } - - callbacks match { - case null => false - case cs if cs.isEmpty => true - case cs => - executor dispatchFuture { - () => cs.foreach(f => notifyCompleted(f, value)) - } - true - } - } - - def onComplete[U](func: Try[T] => U): this.type = { - @tailrec // Returns whether the future has already been completed or not - def tryAddCallback(): Boolean = { - val cur = getState - cur match { - case _: Success[_] | _: Failure[_] => true - case p: Pending[_] => - val pt = p.asInstanceOf[Pending[T]] - if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false else tryAddCallback() - } - } - - if (tryAddCallback()) { - val result = value.get - executor dispatchFuture { - () => notifyCompleted(func, result) - } - } - - this - } - - private final def notifyCompleted(func: Try[T] => Any, result: Try[T]) { - try { - func(result) - } catch { - case e => executor.reportFailure(e) - } - } - } - - /** An already completed Future is given its result at creation. - * - * Useful in Future-composition when a value to contribute is already available. - */ - final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { - val value = Some(resolve(suppliedValue)) - - def tryComplete(value: Try[T]): Boolean = false - - def onComplete[U](func: Try[T] => U): this.type = { - val completedAs = value.get - executor dispatchFuture { - () => func(completedAs) - } - this - } - - private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this - - def await(atMost: Duration)(implicit permit: CanAwait): T = value.get match { - case util.Failure(e) => throw e - case util.Success(r) => r - } - } - -} - - - - - - - - - - - - - - - - diff --git a/src/library/scala/concurrent/akka/package.scala b/src/library/scala/concurrent/akka/package.scala deleted file mode 100644 index 8c059b8e71..0000000000 --- a/src/library/scala/concurrent/akka/package.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent - - - -import java.{lang => jl} -import scala.util.Duration - - - -package object akka { - - private val toBoxed = Map[Class[_], Class[_]]( - classOf[Boolean] -> classOf[jl.Boolean], - classOf[Byte] -> classOf[jl.Byte], - classOf[Char] -> classOf[jl.Character], - classOf[Short] -> classOf[jl.Short], - classOf[Int] -> classOf[jl.Integer], - classOf[Long] -> classOf[jl.Long], - classOf[Float] -> classOf[jl.Float], - classOf[Double] -> classOf[jl.Double], - classOf[Unit] -> classOf[scala.runtime.BoxedUnit]) - - def boxedType(c: Class[_]): Class[_] = { - if (c.isPrimitive) toBoxed(c) else c - } - - def dur2long(dur: Duration): Long = if (dur.isFinite) dur.toNanos else Long.MaxValue - -} - - diff --git a/src/library/scala/concurrent/default/SchedulerImpl.scala b/src/library/scala/concurrent/default/SchedulerImpl.scala deleted file mode 100644 index 745d2d1a15..0000000000 --- a/src/library/scala/concurrent/default/SchedulerImpl.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent -package default - -import scala.util.Duration - -private[concurrent] final class SchedulerImpl extends Scheduler { - private val timer = - new java.util.Timer(true) // the associated thread runs as a daemon - - def schedule(delay: Duration, frequency: Duration)(thunk: => Unit): Cancellable = ??? - - def scheduleOnce(delay: Duration, task: Runnable): Cancellable = { - val timerTask = new java.util.TimerTask { - def run(): Unit = - task.run() - } - timer.schedule(timerTask, delay.toMillis) - new Cancellable { - def cancel(): Unit = - timerTask.cancel() - } - } - - def scheduleOnce(delay: Duration)(task: => Unit): Cancellable = { - val timerTask = new java.util.TimerTask { - def run(): Unit = - task - } - timer.schedule(timerTask, delay.toMillis) - new Cancellable { - def cancel(): Unit = - timerTask.cancel() - } - } - -} diff --git a/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled b/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled new file mode 100644 index 0000000000..745d2d1a15 --- /dev/null +++ b/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled @@ -0,0 +1,44 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent +package default + +import scala.util.Duration + +private[concurrent] final class SchedulerImpl extends Scheduler { + private val timer = + new java.util.Timer(true) // the associated thread runs as a daemon + + def schedule(delay: Duration, frequency: Duration)(thunk: => Unit): Cancellable = ??? + + def scheduleOnce(delay: Duration, task: Runnable): Cancellable = { + val timerTask = new java.util.TimerTask { + def run(): Unit = + task.run() + } + timer.schedule(timerTask, delay.toMillis) + new Cancellable { + def cancel(): Unit = + timerTask.cancel() + } + } + + def scheduleOnce(delay: Duration)(task: => Unit): Cancellable = { + val timerTask = new java.util.TimerTask { + def run(): Unit = + task + } + timer.schedule(timerTask, delay.toMillis) + new Cancellable { + def cancel(): Unit = + timerTask.cancel() + } + } + +} diff --git a/src/library/scala/concurrent/default/TaskImpl.scala b/src/library/scala/concurrent/default/TaskImpl.scala deleted file mode 100644 index 94e54cb372..0000000000 --- a/src/library/scala/concurrent/default/TaskImpl.scala +++ /dev/null @@ -1,313 +0,0 @@ -package scala.concurrent -package default - - - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater -import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } -import scala.util.Try -import scala.util -import scala.util.Duration -import scala.annotation.tailrec - - - -private[concurrent] trait Completable[T] { -self: Future[T] => - - val executor: ExecutionContextImpl - - def newPromise[S]: Promise[S] = executor promise - - type Callback = Try[T] => Any - - def getState: State[T] - - def casState(oldv: State[T], newv: State[T]): Boolean - - protected def dispatch[U](r: Runnable) = executionContext execute r - - protected def processCallbacks(cbs: List[Callback], r: Try[T]) = - for (cb <- cbs) dispatch(new Runnable { - override def run() = cb(r) - }) - - def future: Future[T] = self - - def onComplete[U](callback: Try[T] => U): this.type = { - @tailrec def tryAddCallback(): Try[T] = { - getState match { - case p @ Pending(lst) => - val pt = p.asInstanceOf[Pending[T]] - if (casState(pt, Pending(callback :: pt.callbacks))) null - else tryAddCallback() - case Success(res) => util.Success(res) - case Failure(t) => util.Failure(t) - } - } - - val res = tryAddCallback() - if (res != null) dispatch(new Runnable { - override def run() = - try callback(res) - catch handledFutureException andThen { - t => Console.err.println(t) - } - }) - - this - } - - def isTimedout: Boolean = getState match { - case Failure(ft: FutureTimeoutException) => true - case _ => false - } - -} - -private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) -extends Promise[T] with Future[T] with Completable[T] { - - val executor: scala.concurrent.default.ExecutionContextImpl = context - - @volatile private var state: State[T] = _ - - val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[PromiseImpl[T]], classOf[State[T]], "state") - - updater.set(this, Pending(List())) - - def casState(oldv: State[T], newv: State[T]): Boolean = { - updater.compareAndSet(this, oldv, newv) - } - - def getState: State[T] = { - updater.get(this) - } - - @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { - case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs - case _ => null - } - - def tryComplete(r: Try[T]) = r match { - case util.Failure(t) => tryFailure(t) - case util.Success(v) => trySuccess(v) - } - - override def trySuccess(value: T): Boolean = { - val cbs = tryCompleteState(Success(value)) - if (cbs == null) - false - else { - processCallbacks(cbs, util.Success(value)) - this.synchronized { - this.notifyAll() - } - true - } - } - - override def tryFailure(t: Throwable): Boolean = { - val wrapped = wrap(t) - val cbs = tryCompleteState(Failure(wrapped)) - if (cbs == null) - false - else { - processCallbacks(cbs, util.Failure(wrapped)) - this.synchronized { - this.notifyAll() - } - true - } - } - - def await(atMost: Duration)(implicit canawait: scala.concurrent.CanAwait): T = getState match { - case Success(res) => res - case Failure(t) => throw t - case _ => - this.synchronized { - while (true) - getState match { - case Pending(_) => this.wait() - case Success(res) => return res - case Failure(t) => throw t - } - } - sys.error("unreachable") - } - -} - -private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) -extends RecursiveAction with Task[T] with Future[T] with Completable[T] { - - val executor: ExecutionContextImpl = context - - @volatile private var state: State[T] = _ - - val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[TaskImpl[T]], classOf[State[T]], "state") - - updater.set(this, Pending(List())) - - def casState(oldv: State[T], newv: State[T]): Boolean = { - updater.compareAndSet(this, oldv, newv) - } - - def getState: State[T] = { - updater.get(this) - } - - @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { - case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs - } - - def compute(): Unit = { - var cbs: List[Callback] = null - try { - val res = body - processCallbacks(tryCompleteState(Success(res)), util.Success(res)) - } catch { - case t if isFutureThrowable(t) => - processCallbacks(tryCompleteState(Failure(t)), util.Failure(t)) - case t => - val ee = new ExecutionException(t) - processCallbacks(tryCompleteState(Failure(ee)), util.Failure(ee)) - throw t - } - } - - def start(): Unit = { - Thread.currentThread match { - case fj: ForkJoinWorkerThread if fj.getPool eq executor.pool => fork() - case _ => executor.pool.execute(this) - } - } - - // TODO FIXME: handle timeouts - def await(atMost: Duration): this.type = - await - - def await: this.type = { - this.join() - this - } - - def tryCancel(): Unit = - tryUnfork() - - def await(atMost: Duration)(implicit canawait: CanAwait): T = { - join() // TODO handle timeout also - (updater.get(this): @unchecked) match { - case Success(r) => r - case Failure(t) => throw t - } - } - -} - - -private[concurrent] sealed abstract class State[T] - - -case class Pending[T](callbacks: List[Try[T] => Any]) extends State[T] - - -case class Success[T](result: T) extends State[T] - - -case class Failure[T](throwable: Throwable) extends State[T] - - -private[concurrent] final class ExecutionContextImpl extends ExecutionContext { - import ExecutionContextImpl._ - - val pool = { - val p = new ForkJoinPool - p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler { - def uncaughtException(t: Thread, throwable: Throwable) { - Console.err.println(throwable.getMessage) - throwable.printStackTrace(Console.err) - } - }) - p - } - - @inline - private def executeTask(task: RecursiveAction) { - if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) - task.fork() - else - pool execute task - } - - def execute(task: Runnable) { - val action = new RecursiveAction { def compute() { task.run() } } - executeTask(action) - } - - def execute[U](body: () => U) { - val action = new RecursiveAction { def compute() { body() } } - executeTask(action) - } - - def task[T](body: => T): Task[T] = { - new TaskImpl(this, body) - } - - def future[T](body: => T): Future[T] = { - val t = task(body) - t.start() - t.future - } - - def promise[T]: Promise[T] = - new PromiseImpl[T](this) - - def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { - currentExecutionContext.get match { - case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case - case x if x eq this => this.blockingCall(awaitable) // inside an execution context thread on this executor - case x => x.blocking(awaitable, atMost) - } - } - - private def blockingCall[T](b: Awaitable[T]): T = b match { - case fj: TaskImpl[_] if fj.executor.pool eq pool => - fj.await(Duration.fromNanos(0)) - case _ => - var res: T = null.asInstanceOf[T] - @volatile var blockingDone = false - // TODO add exception handling here! - val mb = new ForkJoinPool.ManagedBlocker { - def block() = { - res = b.await(Duration.fromNanos(0))(CanAwaitEvidence) - blockingDone = true - true - } - def isReleasable = blockingDone - } - ForkJoinPool.managedBlock(mb, true) - res - } - - def reportFailure(t: Throwable): Unit = {} - -} - - -object ExecutionContextImpl { - - private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { - override protected def initialValue = null - } - -} - - - - - - - diff --git a/src/library/scala/concurrent/default/TaskImpl.scala.disabled b/src/library/scala/concurrent/default/TaskImpl.scala.disabled new file mode 100644 index 0000000000..94e54cb372 --- /dev/null +++ b/src/library/scala/concurrent/default/TaskImpl.scala.disabled @@ -0,0 +1,313 @@ +package scala.concurrent +package default + + + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater +import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } +import scala.util.Try +import scala.util +import scala.util.Duration +import scala.annotation.tailrec + + + +private[concurrent] trait Completable[T] { +self: Future[T] => + + val executor: ExecutionContextImpl + + def newPromise[S]: Promise[S] = executor promise + + type Callback = Try[T] => Any + + def getState: State[T] + + def casState(oldv: State[T], newv: State[T]): Boolean + + protected def dispatch[U](r: Runnable) = executionContext execute r + + protected def processCallbacks(cbs: List[Callback], r: Try[T]) = + for (cb <- cbs) dispatch(new Runnable { + override def run() = cb(r) + }) + + def future: Future[T] = self + + def onComplete[U](callback: Try[T] => U): this.type = { + @tailrec def tryAddCallback(): Try[T] = { + getState match { + case p @ Pending(lst) => + val pt = p.asInstanceOf[Pending[T]] + if (casState(pt, Pending(callback :: pt.callbacks))) null + else tryAddCallback() + case Success(res) => util.Success(res) + case Failure(t) => util.Failure(t) + } + } + + val res = tryAddCallback() + if (res != null) dispatch(new Runnable { + override def run() = + try callback(res) + catch handledFutureException andThen { + t => Console.err.println(t) + } + }) + + this + } + + def isTimedout: Boolean = getState match { + case Failure(ft: FutureTimeoutException) => true + case _ => false + } + +} + +private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) +extends Promise[T] with Future[T] with Completable[T] { + + val executor: scala.concurrent.default.ExecutionContextImpl = context + + @volatile private var state: State[T] = _ + + val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[PromiseImpl[T]], classOf[State[T]], "state") + + updater.set(this, Pending(List())) + + def casState(oldv: State[T], newv: State[T]): Boolean = { + updater.compareAndSet(this, oldv, newv) + } + + def getState: State[T] = { + updater.get(this) + } + + @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { + case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs + case _ => null + } + + def tryComplete(r: Try[T]) = r match { + case util.Failure(t) => tryFailure(t) + case util.Success(v) => trySuccess(v) + } + + override def trySuccess(value: T): Boolean = { + val cbs = tryCompleteState(Success(value)) + if (cbs == null) + false + else { + processCallbacks(cbs, util.Success(value)) + this.synchronized { + this.notifyAll() + } + true + } + } + + override def tryFailure(t: Throwable): Boolean = { + val wrapped = wrap(t) + val cbs = tryCompleteState(Failure(wrapped)) + if (cbs == null) + false + else { + processCallbacks(cbs, util.Failure(wrapped)) + this.synchronized { + this.notifyAll() + } + true + } + } + + def await(atMost: Duration)(implicit canawait: scala.concurrent.CanAwait): T = getState match { + case Success(res) => res + case Failure(t) => throw t + case _ => + this.synchronized { + while (true) + getState match { + case Pending(_) => this.wait() + case Success(res) => return res + case Failure(t) => throw t + } + } + sys.error("unreachable") + } + +} + +private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) +extends RecursiveAction with Task[T] with Future[T] with Completable[T] { + + val executor: ExecutionContextImpl = context + + @volatile private var state: State[T] = _ + + val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[TaskImpl[T]], classOf[State[T]], "state") + + updater.set(this, Pending(List())) + + def casState(oldv: State[T], newv: State[T]): Boolean = { + updater.compareAndSet(this, oldv, newv) + } + + def getState: State[T] = { + updater.get(this) + } + + @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { + case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs + } + + def compute(): Unit = { + var cbs: List[Callback] = null + try { + val res = body + processCallbacks(tryCompleteState(Success(res)), util.Success(res)) + } catch { + case t if isFutureThrowable(t) => + processCallbacks(tryCompleteState(Failure(t)), util.Failure(t)) + case t => + val ee = new ExecutionException(t) + processCallbacks(tryCompleteState(Failure(ee)), util.Failure(ee)) + throw t + } + } + + def start(): Unit = { + Thread.currentThread match { + case fj: ForkJoinWorkerThread if fj.getPool eq executor.pool => fork() + case _ => executor.pool.execute(this) + } + } + + // TODO FIXME: handle timeouts + def await(atMost: Duration): this.type = + await + + def await: this.type = { + this.join() + this + } + + def tryCancel(): Unit = + tryUnfork() + + def await(atMost: Duration)(implicit canawait: CanAwait): T = { + join() // TODO handle timeout also + (updater.get(this): @unchecked) match { + case Success(r) => r + case Failure(t) => throw t + } + } + +} + + +private[concurrent] sealed abstract class State[T] + + +case class Pending[T](callbacks: List[Try[T] => Any]) extends State[T] + + +case class Success[T](result: T) extends State[T] + + +case class Failure[T](throwable: Throwable) extends State[T] + + +private[concurrent] final class ExecutionContextImpl extends ExecutionContext { + import ExecutionContextImpl._ + + val pool = { + val p = new ForkJoinPool + p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler { + def uncaughtException(t: Thread, throwable: Throwable) { + Console.err.println(throwable.getMessage) + throwable.printStackTrace(Console.err) + } + }) + p + } + + @inline + private def executeTask(task: RecursiveAction) { + if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) + task.fork() + else + pool execute task + } + + def execute(task: Runnable) { + val action = new RecursiveAction { def compute() { task.run() } } + executeTask(action) + } + + def execute[U](body: () => U) { + val action = new RecursiveAction { def compute() { body() } } + executeTask(action) + } + + def task[T](body: => T): Task[T] = { + new TaskImpl(this, body) + } + + def future[T](body: => T): Future[T] = { + val t = task(body) + t.start() + t.future + } + + def promise[T]: Promise[T] = + new PromiseImpl[T](this) + + def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { + currentExecutionContext.get match { + case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case + case x if x eq this => this.blockingCall(awaitable) // inside an execution context thread on this executor + case x => x.blocking(awaitable, atMost) + } + } + + private def blockingCall[T](b: Awaitable[T]): T = b match { + case fj: TaskImpl[_] if fj.executor.pool eq pool => + fj.await(Duration.fromNanos(0)) + case _ => + var res: T = null.asInstanceOf[T] + @volatile var blockingDone = false + // TODO add exception handling here! + val mb = new ForkJoinPool.ManagedBlocker { + def block() = { + res = b.await(Duration.fromNanos(0))(CanAwaitEvidence) + blockingDone = true + true + } + def isReleasable = blockingDone + } + ForkJoinPool.managedBlock(mb, true) + res + } + + def reportFailure(t: Throwable): Unit = {} + +} + + +object ExecutionContextImpl { + + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { + override protected def initialValue = null + } + +} + + + + + + + diff --git a/src/library/scala/concurrent/impl/AbstractPromise.java b/src/library/scala/concurrent/impl/AbstractPromise.java new file mode 100644 index 0000000000..5280d67854 --- /dev/null +++ b/src/library/scala/concurrent/impl/AbstractPromise.java @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent.impl; + + + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + + + +abstract class AbstractPromise { + private volatile Object _ref = null; + protected final static AtomicReferenceFieldUpdater updater = + AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); +} diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala new file mode 100644 index 0000000000..af0eb66292 --- /dev/null +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -0,0 +1,134 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent.impl + + + +import java.util.concurrent.{Callable, ExecutorService} +import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} +import scala.util.{ Duration, Try, Success, Failure } +import scala.collection.mutable.Stack + + + +class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionContext { + import ExecutionContextImpl._ + + def execute(runnable: Runnable): Unit = executorService match { + // case fj: ForkJoinPool => + // TODO fork if more applicable + // executorService execute runnable + case _ => + executorService execute runnable + } + + def execute[U](body: () => U): Unit = execute(new Runnable { + def run() = body() + }) + + def promise[T]: Promise[T] = new Promise.DefaultPromise[T]()(this) + + def future[T](body: =>T): Future[T] = { + val p = promise[T] + + dispatchFuture { + () => + p complete { + try { + Success(body) + } catch { + case e => resolver(e) + } + } + } + + p.future + } + + def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { + currentExecutionContext.get match { + case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case + case x => x.blockingCall(awaitable) // inside an execution context thread + } + } + + def reportFailure(t: Throwable) = t match { + case e: Error => throw e // rethrow serious errors + case t => t.printStackTrace() + } + + /** Only callable from the tasks running on the same execution context. */ + private def blockingCall[T](body: Awaitable[T]): T = { + releaseStack() + + // TODO see what to do with timeout + body.await(Duration.fromNanos(0))(CanAwaitEvidence) + } + + // an optimization for batching futures + // TODO we should replace this with a public queue, + // so that it can be stolen from + // OR: a push to the local task queue should be so cheap that this is + // not even needed, but stealing is still possible + private val _taskStack = new ThreadLocal[Stack[() => Unit]]() + + private def releaseStack(): Unit = + _taskStack.get match { + case stack if (stack ne null) && stack.nonEmpty => + val tasks = stack.elems + stack.clear() + _taskStack.remove() + dispatchFuture(() => _taskStack.get.elems = tasks, true) + case null => + // do nothing - there is no local batching stack anymore + case _ => + _taskStack.remove() + } + + private[impl] def dispatchFuture(task: () => Unit, force: Boolean = false): Unit = + _taskStack.get match { + case stack if (stack ne null) && !force => stack push task + case _ => this.execute( + new Runnable { + def run() { + try { + val taskStack = Stack[() => Unit](task) + _taskStack set taskStack + while (taskStack.nonEmpty) { + val next = taskStack.pop() + try { + next.apply() + } catch { + case e => + // TODO catching all and continue isn't good for OOME + reportFailure(e) + } + } + } finally { + _taskStack.remove() + } + } + } + ) + } + +} + + +object ExecutionContextImpl { + + private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContextImpl] = new ThreadLocal[ExecutionContextImpl] { + override protected def initialValue = null + } + +} + + diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala new file mode 100644 index 0000000000..3664241ec0 --- /dev/null +++ b/src/library/scala/concurrent/impl/Future.scala @@ -0,0 +1,77 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent.impl + + + +import scala.concurrent.{Awaitable, ExecutionContext} +import scala.util.{ Try, Success, Failure } +//import scala.util.continuations._ + + + +trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { + + implicit def executor: ExecutionContextImpl + + /** For use only within a Future.flow block or another compatible Delimited Continuations reset block. + * + * Returns the result of this Future without blocking, by suspending execution and storing it as a + * continuation until the result is available. + */ + //def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T => Future[Any])) + + /** Tests whether this Future has been completed. + */ + final def isCompleted: Boolean = value.isDefined + + /** The contained value of this Future. Before this Future is completed + * the value will be None. After completion the value will be Some(Right(t)) + * if it contains a valid result, or Some(Left(error)) if it contains + * an exception. + */ + def value: Option[Try[T]] + + def onComplete[U](func: Try[T] => U): this.type + + /** Creates a new Future[A] which is completed with this Future's result if + * that conforms to A's erased type or a ClassCastException otherwise. + */ + final def mapTo[T](implicit m: Manifest[T]) = { + val p = executor.promise[T] + + onComplete { + case f @ Failure(t) => p complete f.asInstanceOf[Try[T]] + case Success(v) => + p complete (try { + Success(boxedType(m.erasure).cast(v).asInstanceOf[T]) + } catch { + case e: ClassCastException ⇒ Failure(e) + }) + } + + p.future + } + + /** Used by for-comprehensions. + */ + final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p) + + final class FutureWithFilter[+A](self: Future[A], p: A => Boolean) { + def foreach(f: A => Unit): Unit = self filter p foreach f + def map[B](f: A => B) = self filter p map f + def flatMap[B](f: A => Future[B]) = self filter p flatMap f + def withFilter(q: A => Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) + } + +} + + + + diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala new file mode 100644 index 0000000000..3f9970b178 --- /dev/null +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -0,0 +1,251 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent.impl + + + +import java.util.concurrent.TimeUnit.{ NANOSECONDS, MILLISECONDS } +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater +import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} +//import scala.util.continuations._ +import scala.util.Duration +import scala.util.Try +import scala.util +import scala.annotation.tailrec +//import scala.concurrent.NonDeterministic + + + +trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { + + def future = this + + def newPromise[S]: Promise[S] = executor promise + + // TODO refine answer and return types here from Any to type parameters + // then move this up in the hierarchy + /* + final def <<(value: T): Future[T] @cps[Future[Any]] = shift { + cont: (Future[T] => Future[Any]) => + cont(complete(Right(value))) + } + + final def <<(other: Future[T]): Future[T] @cps[Future[Any]] = shift { + cont: (Future[T] => Future[Any]) => + val p = executor.promise[Any] + val thisPromise = this + + thisPromise completeWith other + thisPromise onComplete { v => + try { + p completeWith cont(thisPromise) + } catch { + case e => p complete resolver(e) + } + } + + p.future + } + */ + // TODO finish this once we introduce something like dataflow streams + + /* + final def <<(stream: PromiseStreamOut[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => + val fr = executor.promise[Any] + val f = stream.dequeue(this) + f.onComplete { _ => + try { + fr completeWith cont(f) + } catch { + case e => + fr failure e + } + } + fr + } + */ + +} + + +object Promise { + + def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] + + /** Represents the internal state. + */ + sealed trait FState[+T] { def value: Option[Try[T]] } + + case class Pending[T](listeners: List[Try[T] => Any] = Nil) extends FState[T] { + def value: Option[Try[T]] = None + } + + case class Success[T](value: Option[util.Success[T]] = None) extends FState[T] { + def result: T = value.get.get + } + + case class Failure[T](value: Option[util.Failure[T]] = None) extends FState[T] { + def exception: Throwable = value.get.exception + } + + private val emptyPendingValue = Pending[Nothing](Nil) + + /** Default promise implementation. + */ + class DefaultPromise[T](implicit val executor: ExecutionContextImpl) extends AbstractPromise with Promise[T] { + self => + + updater.set(this, Promise.EmptyPending()) + + protected final def tryAwait(atMost: Duration): Boolean = { + @tailrec + def awaitUnsafe(waitTimeNanos: Long): Boolean = { + if (value.isEmpty && waitTimeNanos > 0) { + val ms = NANOSECONDS.toMillis(waitTimeNanos) + val ns = (waitTimeNanos % 1000000l).toInt // as per object.wait spec + val start = System.nanoTime() + try { + synchronized { + while (value.isEmpty) wait(ms, ns) + } + } catch { + case e: InterruptedException => + } + + awaitUnsafe(waitTimeNanos - (System.nanoTime() - start)) + } else + value.isDefined + } + + executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), Duration.fromNanos(0)) + } + + private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = + if (value.isDefined || tryAwait(atMost)) this + else throw new TimeoutException("Futures timed out after [" + atMost.toMillis + "] milliseconds") + + def await(atMost: Duration)(implicit permit: CanAwait): T = + ready(atMost).value.get match { + case util.Failure(e) => throw e + case util.Success(r) => r + } + + def value: Option[Try[T]] = getState.value + + @inline + private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]] + + @inline + protected final def updateState(oldState: FState[T], newState: FState[T]): Boolean = updater.compareAndSet(this, oldState, newState) + + @inline + protected final def getState: FState[T] = updater.get(this) + + def tryComplete(value: Try[T]): Boolean = { + val callbacks: List[Try[T] => Any] = { + try { + @tailrec + def tryComplete(v: Try[T]): List[Try[T] => Any] = { + getState match { + case cur @ Pending(listeners) => + if (updateState(cur, if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) else Success(Some(v.asInstanceOf[util.Success[T]])))) listeners + else tryComplete(v) + case _ => null + } + } + tryComplete(resolve(value)) + } finally { + synchronized { notifyAll() } // notify any blockers from `tryAwait` + } + } + + callbacks match { + case null => false + case cs if cs.isEmpty => true + case cs => + executor dispatchFuture { + () => cs.foreach(f => notifyCompleted(f, value)) + } + true + } + } + + def onComplete[U](func: Try[T] => U): this.type = { + @tailrec // Returns whether the future has already been completed or not + def tryAddCallback(): Boolean = { + val cur = getState + cur match { + case _: Success[_] | _: Failure[_] => true + case p: Pending[_] => + val pt = p.asInstanceOf[Pending[T]] + if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false else tryAddCallback() + } + } + + if (tryAddCallback()) { + val result = value.get + executor dispatchFuture { + () => notifyCompleted(func, result) + } + } + + this + } + + private final def notifyCompleted(func: Try[T] => Any, result: Try[T]) { + try { + func(result) + } catch { + case e => executor.reportFailure(e) + } + } + } + + /** An already completed Future is given its result at creation. + * + * Useful in Future-composition when a value to contribute is already available. + */ + final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { + val value = Some(resolve(suppliedValue)) + + def tryComplete(value: Try[T]): Boolean = false + + def onComplete[U](func: Try[T] => U): this.type = { + val completedAs = value.get + executor dispatchFuture { + () => func(completedAs) + } + this + } + + private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this + + def await(atMost: Duration)(implicit permit: CanAwait): T = value.get match { + case util.Failure(e) => throw e + case util.Success(r) => r + } + } + +} + + + + + + + + + + + + + + + + diff --git a/src/library/scala/concurrent/impl/package.scala b/src/library/scala/concurrent/impl/package.scala new file mode 100644 index 0000000000..72add73167 --- /dev/null +++ b/src/library/scala/concurrent/impl/package.scala @@ -0,0 +1,39 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent + + + +import java.{lang => jl} +import scala.util.Duration + + + +package object impl { + + private val toBoxed = Map[Class[_], Class[_]]( + classOf[Boolean] -> classOf[jl.Boolean], + classOf[Byte] -> classOf[jl.Byte], + classOf[Char] -> classOf[jl.Character], + classOf[Short] -> classOf[jl.Short], + classOf[Int] -> classOf[jl.Integer], + classOf[Long] -> classOf[jl.Long], + classOf[Float] -> classOf[jl.Float], + classOf[Double] -> classOf[jl.Double], + classOf[Unit] -> classOf[scala.runtime.BoxedUnit]) + + def boxedType(c: Class[_]): Class[_] = { + if (c.isPrimitive) toBoxed(c) else c + } + + def dur2long(dur: Duration): Long = if (dur.isFinite) dur.toNanos else Long.MaxValue + +} + + diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 35b8cf6664..0725332c5e 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -25,12 +25,12 @@ package object concurrent { /** A global execution environment for executing lightweight tasks. */ lazy val executionContext = - new akka.ExecutionContextImpl(java.util.concurrent.Executors.newCachedThreadPool()) + new impl.ExecutionContextImpl(java.util.concurrent.Executors.newCachedThreadPool()) /** A global service for scheduling tasks for execution. */ - lazy val scheduler = - new default.SchedulerImpl + // lazy val scheduler = + // new default.SchedulerImpl val handledFutureException: PartialFunction[Throwable, Throwable] = { case t: Throwable if isFutureThrowable(t) => t diff --git a/test/disabled/jvm/scala-concurrent-tck-akka.scala b/test/disabled/jvm/scala-concurrent-tck-akka.scala new file mode 100644 index 0000000000..dfd906e59e --- /dev/null +++ b/test/disabled/jvm/scala-concurrent-tck-akka.scala @@ -0,0 +1,391 @@ + + +import akka.dispatch.{ + Future => future, + Promise => promise +} +import akka.dispatch.Await.{result => await} + +// Duration required for await +import akka.util.Duration +import java.util.concurrent.TimeUnit +import TimeUnit._ + +import scala.concurrent.{ + TimeoutException, + SyncVar, + ExecutionException +} +//import scala.concurrent.future +//import scala.concurrent.promise +//import scala.concurrent.await + + + +trait TestBase { + + implicit val disp = akka.actor.ActorSystem().dispatcher + + def once(body: (() => Unit) => Unit) { + val sv = new SyncVar[Boolean] + body(() => sv put true) + sv.take() + } + +} + + +trait FutureCallbacks extends TestBase { + + def testOnSuccess(): Unit = once { + done => + var x = 0 + val f = future { + x = 1 + } + f onSuccess { case any => + done() + assert(x == 1) + } + } + + def testOnSuccessWhenCompleted(): Unit = once { + done => + var x = 0 + val f = future { + x = 1 + } + f onSuccess { case any => + assert(x == 1) + x = 2 + f onSuccess { case any => + assert(x == 2) + done() + } + } + } + + def testOnSuccessWhenFailed(): Unit = once { + done => + val f = future[Unit] { + done() + throw new Exception + } + f onSuccess { case any => + assert(false) + } + } + + def testOnFailure(): Unit = once { + done => + var x = 0 + val f = future[Unit] { + x = 1 + throw new Exception + } + f onSuccess { case any => + done() + assert(false) + } + f onFailure { + case _ => + done() + assert(x == 1) + } + } + + def testOnFailureWhenSpecialThrowable(num: Int, cause: Throwable): Unit = once { + done => + val f = future[Unit] { + throw cause + } + f onSuccess { case any => + done() + assert(false) + } + f onFailure { + case e: ExecutionException if (e.getCause == cause) => + done() + case _ => + done() + assert(false) + } + } + + def testOnFailureWhenTimeoutException(): Unit = once { + done => + val f = future[Unit] { + throw new TimeoutException() + } + f onSuccess { case any => + done() + assert(false) + } + f onFailure { + case e: TimeoutException => + done() + case other => + done() + assert(false) + } + } + + testOnSuccess() + testOnSuccessWhenCompleted() + testOnSuccessWhenFailed() + testOnFailure() +// testOnFailureWhenSpecialThrowable(5, new Error) +// testOnFailureWhenSpecialThrowable(6, new scala.util.control.ControlThrowable { }) +// testOnFailureWhenSpecialThrowable(7, new InterruptedException) +// testOnFailureWhenTimeoutException() + +} + + +trait FutureCombinators extends TestBase { + + // map: stub + def testMapSuccess(): Unit = once { + done => + done() + } + + def testMapFailure(): Unit = once { + done => + done() + } + + // flatMap: stub + def testFlatMapSuccess(): Unit = once { + done => + done() + } + + def testFlatMapFailure(): Unit = once { + done => + done() + } + + // filter: stub + def testFilterSuccess(): Unit = once { + done => + done() + } + + def testFilterFailure(): Unit = once { + done => + done() + } + + // foreach: stub + def testForeachSuccess(): Unit = once { + done => + done() + } + + def testForeachFailure(): Unit = once { + done => + done() + } + + def testRecoverSuccess(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } recover { + case re: RuntimeException => + "recovered" + } onSuccess { case x => + done() + assert(x == "recovered") + } onFailure { case any => + done() + assert(false) + } + } + + def testRecoverFailure(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } recover { + case te: TimeoutException => "timeout" + } onSuccess { case x => + done() + assert(false) + } onFailure { case any => + done() + assert(any == cause) + } + } + + testMapSuccess() + testMapFailure() + testFlatMapSuccess() + testFlatMapFailure() + testFilterSuccess() + testFilterFailure() + testForeachSuccess() + testForeachFailure() + testRecoverSuccess() + testRecoverFailure() + +} + +/* +trait FutureProjections extends TestBase { + + def testFailedFailureOnComplete(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + f.failed onComplete { + case Right(t) => + assert(t == cause) + done() + case Left(t) => + assert(false) + } + } + + def testFailedFailureOnSuccess(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + f.failed onSuccess { + t => + assert(t == cause) + done() + } + } + + def testFailedSuccessOnComplete(): Unit = once { + done => + val f = future { 0 } + f.failed onComplete { + case Right(t) => + assert(false) + case Left(t) => + assert(t.isInstanceOf[NoSuchElementException]) + done() + } + } + + def testFailedSuccessOnFailure(): Unit = once { + done => + val f = future { 0 } + f.failed onFailure { + case nsee: NoSuchElementException => + done() + } + } + + def testFailedFailureAwait(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + assert(await(0, f.failed) == cause) + done() + } + + def testFailedSuccessAwait(): Unit = once { + done => + val f = future { 0 } + try { + println(await(0, f.failed)) + assert(false) + } catch { + case nsee: NoSuchElementException => done() + } + } + + testFailedFailureOnComplete() + testFailedFailureOnSuccess() + testFailedSuccessOnComplete() + testFailedSuccessOnFailure() + testFailedFailureAwait() + //testFailedSuccessAwait() + +} +*/ + +trait Blocking extends TestBase { + + def testAwaitSuccess(): Unit = once { + done => + val f = future { 0 } + await(f, Duration(500, "ms")) + done() + } + + def testAwaitFailure(): Unit = once { + done => + val cause = new RuntimeException + val f = future { + throw cause + } + try { + await(f, Duration(500, "ms")) + assert(false) + } catch { + case t => + assert(t == cause) + done() + } + } + + testAwaitSuccess() + testAwaitFailure() + +} + +/* +trait Promises extends TestBase { + + def testSuccess(): Unit = once { + done => + val p = promise[Int]() + val f = p.future + + f.onSuccess { x => + done() + assert(x == 5) + } onFailure { case any => + done() + assert(false) + } + + p.success(5) + } + + testSuccess() + +} +*/ + +trait Exceptions extends TestBase { + +} + + +object Test +extends App +with FutureCallbacks +with FutureCombinators +/*with FutureProjections*/ +/*with Promises*/ +with Blocking +with Exceptions +{ + System.exit(0) +} + + diff --git a/test/files/jvm/concurrent-future.scala b/test/files/jvm/concurrent-future.scala index eb3bbad591..b44d054219 100644 --- a/test/files/jvm/concurrent-future.scala +++ b/test/files/jvm/concurrent-future.scala @@ -22,7 +22,7 @@ object Test extends App { val f = future { output(1, "hai world") } - f onSuccess { _ => + f onSuccess { case _ => output(1, "kthxbye") done() } @@ -33,9 +33,9 @@ object Test extends App { val f = future { output(2, "hai world") } - f onSuccess { _ => + f onSuccess { case _ => output(2, "awsum thx") - f onSuccess { _ => + f onSuccess { case _ => output(2, "kthxbye") done() } @@ -49,7 +49,7 @@ object Test extends App { done() throw new Exception } - f onSuccess { _ => + f onSuccess { case _ => output(3, "onoes") } } @@ -60,7 +60,7 @@ object Test extends App { output(4, "hai world") throw new Exception } - f onSuccess { _ => + f onSuccess { case _ => output(4, "onoes") done() } @@ -76,7 +76,7 @@ object Test extends App { output(num, "hai world") throw cause } - f onSuccess { _ => + f onSuccess { case _ => output(num, "onoes") done() } @@ -96,7 +96,7 @@ object Test extends App { output(8, "hai world") throw new FutureTimeoutException(null) } - f onSuccess { _ => + f onSuccess { case _ => output(8, "onoes") done() } diff --git a/test/files/jvm/scala-concurrent-tck-akka.scala b/test/files/jvm/scala-concurrent-tck-akka.scala deleted file mode 100644 index dfd906e59e..0000000000 --- a/test/files/jvm/scala-concurrent-tck-akka.scala +++ /dev/null @@ -1,391 +0,0 @@ - - -import akka.dispatch.{ - Future => future, - Promise => promise -} -import akka.dispatch.Await.{result => await} - -// Duration required for await -import akka.util.Duration -import java.util.concurrent.TimeUnit -import TimeUnit._ - -import scala.concurrent.{ - TimeoutException, - SyncVar, - ExecutionException -} -//import scala.concurrent.future -//import scala.concurrent.promise -//import scala.concurrent.await - - - -trait TestBase { - - implicit val disp = akka.actor.ActorSystem().dispatcher - - def once(body: (() => Unit) => Unit) { - val sv = new SyncVar[Boolean] - body(() => sv put true) - sv.take() - } - -} - - -trait FutureCallbacks extends TestBase { - - def testOnSuccess(): Unit = once { - done => - var x = 0 - val f = future { - x = 1 - } - f onSuccess { case any => - done() - assert(x == 1) - } - } - - def testOnSuccessWhenCompleted(): Unit = once { - done => - var x = 0 - val f = future { - x = 1 - } - f onSuccess { case any => - assert(x == 1) - x = 2 - f onSuccess { case any => - assert(x == 2) - done() - } - } - } - - def testOnSuccessWhenFailed(): Unit = once { - done => - val f = future[Unit] { - done() - throw new Exception - } - f onSuccess { case any => - assert(false) - } - } - - def testOnFailure(): Unit = once { - done => - var x = 0 - val f = future[Unit] { - x = 1 - throw new Exception - } - f onSuccess { case any => - done() - assert(false) - } - f onFailure { - case _ => - done() - assert(x == 1) - } - } - - def testOnFailureWhenSpecialThrowable(num: Int, cause: Throwable): Unit = once { - done => - val f = future[Unit] { - throw cause - } - f onSuccess { case any => - done() - assert(false) - } - f onFailure { - case e: ExecutionException if (e.getCause == cause) => - done() - case _ => - done() - assert(false) - } - } - - def testOnFailureWhenTimeoutException(): Unit = once { - done => - val f = future[Unit] { - throw new TimeoutException() - } - f onSuccess { case any => - done() - assert(false) - } - f onFailure { - case e: TimeoutException => - done() - case other => - done() - assert(false) - } - } - - testOnSuccess() - testOnSuccessWhenCompleted() - testOnSuccessWhenFailed() - testOnFailure() -// testOnFailureWhenSpecialThrowable(5, new Error) -// testOnFailureWhenSpecialThrowable(6, new scala.util.control.ControlThrowable { }) -// testOnFailureWhenSpecialThrowable(7, new InterruptedException) -// testOnFailureWhenTimeoutException() - -} - - -trait FutureCombinators extends TestBase { - - // map: stub - def testMapSuccess(): Unit = once { - done => - done() - } - - def testMapFailure(): Unit = once { - done => - done() - } - - // flatMap: stub - def testFlatMapSuccess(): Unit = once { - done => - done() - } - - def testFlatMapFailure(): Unit = once { - done => - done() - } - - // filter: stub - def testFilterSuccess(): Unit = once { - done => - done() - } - - def testFilterFailure(): Unit = once { - done => - done() - } - - // foreach: stub - def testForeachSuccess(): Unit = once { - done => - done() - } - - def testForeachFailure(): Unit = once { - done => - done() - } - - def testRecoverSuccess(): Unit = once { - done => - val cause = new RuntimeException - val f = future { - throw cause - } recover { - case re: RuntimeException => - "recovered" - } onSuccess { case x => - done() - assert(x == "recovered") - } onFailure { case any => - done() - assert(false) - } - } - - def testRecoverFailure(): Unit = once { - done => - val cause = new RuntimeException - val f = future { - throw cause - } recover { - case te: TimeoutException => "timeout" - } onSuccess { case x => - done() - assert(false) - } onFailure { case any => - done() - assert(any == cause) - } - } - - testMapSuccess() - testMapFailure() - testFlatMapSuccess() - testFlatMapFailure() - testFilterSuccess() - testFilterFailure() - testForeachSuccess() - testForeachFailure() - testRecoverSuccess() - testRecoverFailure() - -} - -/* -trait FutureProjections extends TestBase { - - def testFailedFailureOnComplete(): Unit = once { - done => - val cause = new RuntimeException - val f = future { - throw cause - } - f.failed onComplete { - case Right(t) => - assert(t == cause) - done() - case Left(t) => - assert(false) - } - } - - def testFailedFailureOnSuccess(): Unit = once { - done => - val cause = new RuntimeException - val f = future { - throw cause - } - f.failed onSuccess { - t => - assert(t == cause) - done() - } - } - - def testFailedSuccessOnComplete(): Unit = once { - done => - val f = future { 0 } - f.failed onComplete { - case Right(t) => - assert(false) - case Left(t) => - assert(t.isInstanceOf[NoSuchElementException]) - done() - } - } - - def testFailedSuccessOnFailure(): Unit = once { - done => - val f = future { 0 } - f.failed onFailure { - case nsee: NoSuchElementException => - done() - } - } - - def testFailedFailureAwait(): Unit = once { - done => - val cause = new RuntimeException - val f = future { - throw cause - } - assert(await(0, f.failed) == cause) - done() - } - - def testFailedSuccessAwait(): Unit = once { - done => - val f = future { 0 } - try { - println(await(0, f.failed)) - assert(false) - } catch { - case nsee: NoSuchElementException => done() - } - } - - testFailedFailureOnComplete() - testFailedFailureOnSuccess() - testFailedSuccessOnComplete() - testFailedSuccessOnFailure() - testFailedFailureAwait() - //testFailedSuccessAwait() - -} -*/ - -trait Blocking extends TestBase { - - def testAwaitSuccess(): Unit = once { - done => - val f = future { 0 } - await(f, Duration(500, "ms")) - done() - } - - def testAwaitFailure(): Unit = once { - done => - val cause = new RuntimeException - val f = future { - throw cause - } - try { - await(f, Duration(500, "ms")) - assert(false) - } catch { - case t => - assert(t == cause) - done() - } - } - - testAwaitSuccess() - testAwaitFailure() - -} - -/* -trait Promises extends TestBase { - - def testSuccess(): Unit = once { - done => - val p = promise[Int]() - val f = p.future - - f.onSuccess { x => - done() - assert(x == 5) - } onFailure { case any => - done() - assert(false) - } - - p.success(5) - } - - testSuccess() - -} -*/ - -trait Exceptions extends TestBase { - -} - - -object Test -extends App -with FutureCallbacks -with FutureCombinators -/*with FutureProjections*/ -/*with Promises*/ -with Blocking -with Exceptions -{ - System.exit(0) -} - - diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index a951c09da2..244ff02da7 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -11,6 +11,7 @@ import scala.concurrent.{ import scala.concurrent.future import scala.concurrent.promise import scala.concurrent.blocking +import scala.util.{ Try, Success, Failure } import scala.util.Duration @@ -23,13 +24,13 @@ trait TestBase { sv.take() } - def assert(cond: => Boolean) { - try { - Predef.assert(cond) - } catch { - case e => e.printStackTrace() - } - } + // def assert(cond: => Boolean) { + // try { + // Predef.assert(cond) + // } catch { + // case e => e.printStackTrace() + // } + // } } @@ -264,10 +265,10 @@ trait FutureProjections extends TestBase { throw cause } f.failed onComplete { - case Right(t) => + case Success(t) => assert(t == cause) done() - case Left(t) => + case Failure(t) => assert(false) } } @@ -289,9 +290,9 @@ trait FutureProjections extends TestBase { done => val f = future { 0 } f.failed onComplete { - case Right(t) => + case Success(t) => assert(false) - case Left(t) => + case Failure(t) => assert(t.isInstanceOf[NoSuchElementException]) done() } diff --git a/test/files/pos/Transactions.scala b/test/files/pos/Transactions.scala index 9b4388300b..525eff7514 100644 --- a/test/files/pos/Transactions.scala +++ b/test/files/pos/Transactions.scala @@ -1,4 +1,4 @@ -package scala.concurrent +package scala.concurrent1 class AbortException extends RuntimeException -- cgit v1.2.3 From 0288a0788ae1ffbe9064992f1ac02f647bb64c17 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 17 Feb 2012 13:57:06 -0800 Subject: Fix for "type _$1 defined twice". Actually not a fix, only a workaround. Package objects strike again: you don't want to have existentials in there, like this: private val resolverFunction: PartialFunction[Throwable, Try[_]] = ... There are a few irrelevant-to-that-bug changes included which I did while debugging and figured I'd keep them. With this patch I can merge/am merging the scala.concurrent work. --- build.xml | 2 + .../scala/tools/nsc/ast/parser/Parsers.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 4 + .../scala/concurrent/ConcurrentPackageObject.scala | 103 +++++++++++++++++++++ src/library/scala/concurrent/impl/Future.scala | 24 +++-- src/library/scala/concurrent/impl/Promise.scala | 1 + src/library/scala/concurrent/impl/package.scala | 39 -------- src/library/scala/concurrent/package.scala | 103 +-------------------- 8 files changed, 134 insertions(+), 144 deletions(-) create mode 100644 src/library/scala/concurrent/ConcurrentPackageObject.scala delete mode 100644 src/library/scala/concurrent/impl/package.scala (limited to 'src') diff --git a/build.xml b/build.xml index 79b36fd4a6..314ce379e1 100644 --- a/build.xml +++ b/build.xml @@ -1535,6 +1535,7 @@ DOCUMENTATION docUncompilable="${src.dir}/library-aux" sourcepath="${src.dir}" classpathref="pack.classpath" + addparams="${scalac.args.all}" docRootContent="${src.dir}/library/rootdoc.txt"> @@ -1620,6 +1621,7 @@ DOCUMENTATION classpathref="pack.classpath" srcdir="${src.dir}/compiler" docRootContent="${src.dir}/compiler/rootdoc.txt"> + addparams="${scalac.args.all}" diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index fe6dcc9138..e3a59058a3 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1205,7 +1205,7 @@ self => */ def wildcardType(start: Int) = { val pname = freshTypeName("_$") - val t = atPos(start) { Ident(pname) } + val t = atPos(start)(Ident(pname)) val bounds = typeBounds() val param = atPos(t.pos union bounds.pos) { makeSyntheticTypeParam(pname, bounds) } placeholderTypes = param :: placeholderTypes diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 23fc30b9fb..e7cd139ce0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2169,6 +2169,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // error for this is issued in RefChecks.checkDefaultsInOverloaded if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag && !e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) { + log("Double definition detected:\n " + + ((e.sym.getClass, e.sym.info, e.sym.ownerChain)) + "\n " + + ((e1.sym.getClass, e1.sym.info, e1.sym.ownerChain))) + DefDefinedTwiceError(e.sym, e1.sym) scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779 } diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala new file mode 100644 index 0000000000..6aacd53de2 --- /dev/null +++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala @@ -0,0 +1,103 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent + +import scala.util.{ Duration, Try, Success, Failure } +import ConcurrentPackageObject._ + +/** This package object contains primitives for concurrent and parallel programming. + */ +abstract class ConcurrentPackageObject { + /** A global execution environment for executing lightweight tasks. + */ + lazy val executionContext = + new impl.ExecutionContextImpl(java.util.concurrent.Executors.newCachedThreadPool()) + + /** A global service for scheduling tasks for execution. + */ + // lazy val scheduler = + // new default.SchedulerImpl + + val handledFutureException: PartialFunction[Throwable, Throwable] = { + case t: Throwable if isFutureThrowable(t) => t + } + + // TODO rename appropriately and make public + private[concurrent] def isFutureThrowable(t: Throwable) = t match { + case e: Error => false + case t: scala.util.control.ControlThrowable => false + case i: InterruptedException => false + case _ => true + } + + private[concurrent] def resolve[T](source: Try[T]): Try[T] = source match { + case Failure(t: scala.runtime.NonLocalReturnControl[_]) => Success(t.value.asInstanceOf[T]) + case Failure(t: scala.util.control.ControlThrowable) => Failure(new ExecutionException("Boxed ControlThrowable", t)) + case Failure(t: InterruptedException) => Failure(new ExecutionException("Boxed InterruptedException", t)) + case Failure(e: Error) => Failure(new ExecutionException("Boxed Error", e)) + case _ => source + } + + private[concurrent] def resolver[T] = + resolverFunction.asInstanceOf[PartialFunction[Throwable, Try[T]]] + + /* concurrency constructs */ + + def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = + execCtx future body + + def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] = + execCtx promise + + /** Wraps a block of code into an awaitable object. */ + def body2awaitable[T](body: =>T) = new Awaitable[T] { + def await(atMost: Duration)(implicit cb: CanAwait) = body + } + + /** Used to block on a piece of code which potentially blocks. + * + * @param body A piece of code which contains potentially blocking or long running calls. + * + * Calling this method may throw the following exceptions: + * - CancellationException - if the computation was cancelled + * - InterruptedException - in the case that a wait within the blockable object was interrupted + * - TimeoutException - in the case that the blockable object timed out + */ + def blocking[T](atMost: Duration)(body: =>T)(implicit execCtx: ExecutionContext): T = + executionContext.blocking(atMost)(body) + + /** Blocks on an awaitable object. + * + * @param awaitable An object with a `block` method which runs potentially blocking or long running calls. + * + * Calling this method may throw the following exceptions: + * - CancellationException - if the computation was cancelled + * - InterruptedException - in the case that a wait within the blockable object was interrupted + * - TimeoutException - in the case that the blockable object timed out + */ + def blocking[T](awaitable: Awaitable[T], atMost: Duration)(implicit execCtx: ExecutionContext = executionContext): T = + executionContext.blocking(awaitable, atMost) + + @inline implicit final def int2durationops(x: Int): DurationOps = new DurationOps(x) +} + +private[concurrent] object ConcurrentPackageObject { + // TODO, docs, return type + // Note that having this in the package object led to failures when + // compiling a subset of sources; it seems that the wildcard is not + // properly handled, and you get messages like "type _$1 defined twice". + // This is consistent with other package object breakdowns. + private val resolverFunction: PartialFunction[Throwable, Try[_]] = { + case t: scala.runtime.NonLocalReturnControl[_] => Success(t.value) + case t: scala.util.control.ControlThrowable => Failure(new ExecutionException("Boxed ControlThrowable", t)) + case t: InterruptedException => Failure(new ExecutionException("Boxed InterruptedException", t)) + case e: Error => Failure(new ExecutionException("Boxed Error", e)) + case t => Failure(t) + } +} diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 3664241ec0..24d0258cc8 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -8,14 +8,10 @@ package scala.concurrent.impl - - import scala.concurrent.{Awaitable, ExecutionContext} import scala.util.{ Try, Success, Failure } //import scala.util.continuations._ - - trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { implicit def executor: ExecutionContextImpl @@ -50,7 +46,7 @@ trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { case f @ Failure(t) => p complete f.asInstanceOf[Try[T]] case Success(v) => p complete (try { - Success(boxedType(m.erasure).cast(v).asInstanceOf[T]) + Success(Future.boxedType(m.erasure).cast(v).asInstanceOf[T]) } catch { case e: ClassCastException ⇒ Failure(e) }) @@ -72,6 +68,22 @@ trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { } +object Future { + import java.{ lang => jl } + private val toBoxed = Map[Class[_], Class[_]]( + classOf[Boolean] -> classOf[jl.Boolean], + classOf[Byte] -> classOf[jl.Byte], + classOf[Char] -> classOf[jl.Character], + classOf[Short] -> classOf[jl.Short], + classOf[Int] -> classOf[jl.Integer], + classOf[Long] -> classOf[jl.Long], + classOf[Float] -> classOf[jl.Float], + classOf[Double] -> classOf[jl.Double], + classOf[Unit] -> classOf[scala.runtime.BoxedUnit] + ) - + def boxedType(c: Class[_]): Class[_] = { + if (c.isPrimitive) toBoxed(c) else c + } +} diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 3f9970b178..7ef76e1501 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -75,6 +75,7 @@ trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { object Promise { + def dur2long(dur: Duration): Long = if (dur.isFinite) dur.toNanos else Long.MaxValue def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] diff --git a/src/library/scala/concurrent/impl/package.scala b/src/library/scala/concurrent/impl/package.scala deleted file mode 100644 index 72add73167..0000000000 --- a/src/library/scala/concurrent/impl/package.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent - - - -import java.{lang => jl} -import scala.util.Duration - - - -package object impl { - - private val toBoxed = Map[Class[_], Class[_]]( - classOf[Boolean] -> classOf[jl.Boolean], - classOf[Byte] -> classOf[jl.Byte], - classOf[Char] -> classOf[jl.Character], - classOf[Short] -> classOf[jl.Short], - classOf[Int] -> classOf[jl.Integer], - classOf[Long] -> classOf[jl.Long], - classOf[Float] -> classOf[jl.Float], - classOf[Double] -> classOf[jl.Double], - classOf[Unit] -> classOf[scala.runtime.BoxedUnit]) - - def boxedType(c: Class[_]): Class[_] = { - if (c.isPrimitive) toBoxed(c) else c - } - - def dur2long(dur: Duration): Long = if (dur.isFinite) dur.toNanos else Long.MaxValue - -} - - diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 0725332c5e..6a98fd50c2 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -8,98 +8,17 @@ package scala - - import scala.util.{ Duration, Try, Success, Failure } - - /** This package object contains primitives for concurrent and parallel programming. */ -package object concurrent { - +package object concurrent extends scala.concurrent.ConcurrentPackageObject { type ExecutionException = java.util.concurrent.ExecutionException type CancellationException = java.util.concurrent.CancellationException type TimeoutException = java.util.concurrent.TimeoutException - - /** A global execution environment for executing lightweight tasks. - */ - lazy val executionContext = - new impl.ExecutionContextImpl(java.util.concurrent.Executors.newCachedThreadPool()) - - /** A global service for scheduling tasks for execution. - */ - // lazy val scheduler = - // new default.SchedulerImpl - - val handledFutureException: PartialFunction[Throwable, Throwable] = { - case t: Throwable if isFutureThrowable(t) => t - } - - // TODO rename appropriately and make public - private[concurrent] def isFutureThrowable(t: Throwable) = t match { - case e: Error => false - case t: scala.util.control.ControlThrowable => false - case i: InterruptedException => false - case _ => true - } - - private[concurrent] def resolve[T](source: Try[T]): Try[T] = source match { - case Failure(t: scala.runtime.NonLocalReturnControl[_]) => Success(t.value.asInstanceOf[T]) - case Failure(t: scala.util.control.ControlThrowable) => Failure(new ExecutionException("Boxed ControlThrowable", t)) - case Failure(t: InterruptedException) => Failure(new ExecutionException("Boxed InterruptedException", t)) - case Failure(e: Error) => Failure(new ExecutionException("Boxed Error", e)) - case _ => source - } - - // TODO, docs, return type - private val resolverFunction: PartialFunction[Throwable, Try[_]] = { - case t: scala.runtime.NonLocalReturnControl[_] => Success(t.value) - case t: scala.util.control.ControlThrowable => Failure(new ExecutionException("Boxed ControlThrowable", t)) - case t: InterruptedException => Failure(new ExecutionException("Boxed InterruptedException", t)) - case e: Error => Failure(new ExecutionException("Boxed Error", e)) - case t => Failure(t) - } - - private[concurrent] def resolver[T] = resolverFunction.asInstanceOf[PartialFunction[Throwable, Try[T]]] - - /* concurrency constructs */ - - def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = - execCtx future body - - def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] = - execCtx promise - - /** Wraps a block of code into an awaitable object. */ - def body2awaitable[T](body: =>T) = new Awaitable[T] { - def await(atMost: Duration)(implicit cb: CanAwait) = body - } - - /** Used to block on a piece of code which potentially blocks. - * - * @param body A piece of code which contains potentially blocking or long running calls. - * - * Calling this method may throw the following exceptions: - * - CancellationException - if the computation was cancelled - * - InterruptedException - in the case that a wait within the blockable object was interrupted - * - TimeoutException - in the case that the blockable object timed out - */ - def blocking[T](atMost: Duration)(body: =>T)(implicit execCtx: ExecutionContext): T = - executionContext.blocking(atMost)(body) - - /** Blocks on an awaitable object. - * - * @param awaitable An object with a `block` method which runs potentially blocking or long running calls. - * - * Calling this method may throw the following exceptions: - * - CancellationException - if the computation was cancelled - * - InterruptedException - in the case that a wait within the blockable object was interrupted - * - TimeoutException - in the case that the blockable object timed out - */ - def blocking[T](awaitable: Awaitable[T], atMost: Duration)(implicit execCtx: ExecutionContext = executionContext): T = - executionContext.blocking(awaitable, atMost) - +} + +package concurrent { object await { def ready[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): Awaitable[T] = { try blocking(awaitable, atMost) @@ -119,16 +38,7 @@ package object concurrent { * some programs cannot be written without them (e.g. multiple client threads * cannot send requests to a server thread through regular promises and futures). */ - object nondeterministic { - } - - @inline implicit final def int2durationops(x: Int) = new DurationOps(x) - -} - - - -package concurrent { + object nondeterministic { } /** A timeout exception. * @@ -144,7 +54,4 @@ package concurrent { // TODO ADD OTHERS def ns = util.Duration.fromNanos(0) } - } - - -- cgit v1.2.3