All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rx.core.Propagator.scala Maven / Gradle / Ivy

package rx.core

import concurrent.{Future, ExecutionContext}

object Propagator{
  /**
   * A propagator which runs propagation waves on the given `ExecutionContext`.
   * Exactly how it is done (e.g. serially or in parallel) depends on the
   * `ExecutionContext` given. Returns a `Future` which will complete when
   * the propagation wave is finished. This `Future` can be blocked on if you
   * want to block until the propagation wave is complete before continuing.
   *
   * @param ec The `ExecutionContext` on which the distribute the individual
   *           updates of each propagation wave.
   */
  class ExecContext(implicit ec: ExecutionContext) extends Propagator[Future[Unit]]{
    implicit val pinger = this
    def propagate(nodes: Set[(Emitter[_], Reactor[_])]): Future[Unit] = {
      if (nodes.size != 0){
        val minLevel = nodes.map(_._2.level).min
        val (now, later) = nodes.partition(_._2.level == minLevel)
        val next = now.groupBy(_._2)
                      .mapValues(_.map(_._1))
                      .map{ case (target, pingers) => Future{
          target.ping(pingers).map(target.asInstanceOf[Emitter[Any]] -> _)
        }}

        Future.sequence(next.toSet).map(_.flatten ++ later).flatMap(propagate)
      }else Future.successful(())
    }
  }

  /**
   * A propagator which runs propagation waves on the thread which triggered
   * it. Returns `Unit` when the propagation wave is complete.
   */
  implicit object Immediate extends Propagator[Unit]{
    def propagate(nodes: Set[(Emitter[_], Reactor[_])]): Unit = {
      if (nodes.size != 0){
        val minLevel = nodes.map(_._2.level).min
        val (now, later) = nodes.partition(_._2.level == minLevel)
        val next = now.groupBy(_._2)
                      .mapValues(_.map(_._1))
                      .map{ case (target, pingers) =>
          target.ping(pingers).map(target.asInstanceOf[Emitter[Any]] -> _)
        }
        propagate(next.flatten.toSet ++ later)
      }
    }
  }

  /**
   * Convenience method to retrieve the implicit [[Propagator]] from the
   * enclosing scope.
   */
  def apply[P: Propagator]() = implicitly[Propagator[P]]
}

/**
 * A Propagator is an object which performs a propagation wave over the
 * Scala.Rx dataflow graph. It is parametrized on the type P which it returns
 * after performing a propagation wave. The two existing Propagators are:
 *
 * [[Propagator.Immediate]] (`Propagator[Unit]`), which runs the propagation wave immediately
 * and returns Unit when it is complete.
 *
 * [[Propagator.ExecContext]] (`Propagator[Future[Unit]]`), which runs the propagation wave
 * on the given `ExecutionContext` and returns a `Future[Unit]` representing
 * the completion of the propagation wave.
 *
 * It is conceivable that custom propagators could use the return type `P` to
 * return other things, e.g. the number of updates performed, or the number
 * of re-tries in that propagation wave. That is up to the implementer to
 * decide.
 *
 @tparam P the type that the `propagate()` function returns
 */
trait Propagator[P]{
  /**
   * B  egins a propagation wave, with a set of
   *
   *     Emitter -> Reactor
   *
   * pings. See the implementation of [[Propagator.Immediate]] or
   * [[Propagator.ExecContext]] to see how this is generally done
   *
   * @param pings The set of pings which begin this propagation wave
   * @return Some value of type P, dependent on the implementation
   */
  def propagate(pings: Set[(Emitter[_], Reactor[_])]): P
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy