de.sciss.lucre.Push.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lucre-core_3 Show documentation
Show all versions of lucre-core_3 Show documentation
Extension of Scala-STM, adding optional durability layer, and providing API for confluent and reactive event layers
The newest version!
/*
* Push.scala
* (Lucre 4)
*
* Copyright (c) 2009-2024 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU Affero General Public License v3+
*
*
* For further information, please contact Hanns Holger Rutz at
* [email protected]
*/
package de.sciss.lucre
import de.sciss.equal.Implicits._
import de.sciss.lucre.Log.{event => logEvent}
import scala.annotation.elidable
import scala.annotation.elidable.CONFIG
import scala.collection.immutable.{Map => IMap}
object Push {
private[lucre] def apply[T <: Txn[T], A](origin: Event[T, A], update: A)(implicit tx: T): Unit = {
val push = new Impl(origin, update)
logEvent.debug("push begin")
push.visitChildren(origin)
logEvent.debug("pull begin")
push.pull()
logEvent.debug("pull end")
}
type Parents[T <: Txn[T]] = Set[Event[T, Any]]
private def NoParents[T <: Txn[T]]: Parents[T] = Set.empty[Event[T, Any]]
// private type Visited[T <: Txn[T]] = IMap[Event[T, Any], Parents[T]]
private final class Reaction[T <: Txn[T], +A](update: A, observers: List[Observer[T, A]]) {
def apply()(implicit tx: T): Unit =
observers.foreach(_.apply(update))
}
private final class Impl[T <: Txn[T]](origin: Event[T, Any], val update: Any)(implicit tx: T)
extends Pull[T] {
private[this] var pushMap = IMap(origin -> NoParents[T])
private[this] var pullMap = IMap.empty[EventLike[T, Any], Option[Any]]
private[this] var indent = ""
@elidable(CONFIG) private[this] def incIndent(): Unit = indent += " "
@elidable(CONFIG) private[this] def decIndent(): Unit = indent = indent.substring(2)
private[this] def addVisited(child: Event[T, Any], parent: Event[T, Any]): Boolean = {
val parents = pushMap.getOrElse(child, NoParents)
logEvent.debug(s"${indent}visit $child (new ? ${parents.isEmpty})")
pushMap += ((child, parents + parent))
parents.isEmpty
}
def visitChildren(parent: Event[T, Any]): Unit = {
val inlet = parent.slot
incIndent()
try {
val childEvents = parent.node.getTargets.children
childEvents.foreach { case (inlet2, child) =>
if (inlet2.toInt === inlet) {
visit(child, parent)
}
}
} finally {
decIndent()
}
}
def visit(child: Event[T, Any], parent: Event[T, Any]): Unit =
if (addVisited(child, parent)) visitChildren(child)
// cf. https://stackoverflow.com/questions/32098481
def contains(source: EventLike[T, Any]): Boolean = source match {
case e: Event[T, Any] => pushMap.contains(e)
case _ => false
}
def isOrigin(that : EventLike[T, Any]): Boolean = that === origin
def parents (child : Event [T, Any]): Parents[T] = pushMap.getOrElse(child, NoParents)
def pull(): Unit = {
val reactions: List[Reaction[T, Any]] = pushMap.iterator.flatMap { case (event, _) =>
val observers = tx.reactionMap.getEventReactions(event)
if (observers.nonEmpty || event.isInstanceOf[Caching])
apply[Any](event).map(new Reaction(_, observers)) else None
} .toList
logEvent.debug(s"numReactions = ${reactions.size}")
reactions.foreach(_.apply())
}
def resolve[A]: A = {
logEvent.debug(s"${indent}resolve")
update.asInstanceOf[A]
}
// caches pulled values
def apply[A](source: EventLike[T, A]): Option[A] = {
incIndent()
try {
pullMap.get(source) match {
case Some(res) =>
logEvent.debug(s"${indent}pull $source (new ? false)")
res.asInstanceOf[Option[A]]
case _ =>
logEvent.debug(s"${indent}pull $source (new ? true)")
val res = source.pullUpdate(this)
pullMap += ((source, res))
res
}
} finally {
decIndent()
}
}
}
}
trait Pull[T <: Txn[T]] {
/** Assuming that the caller is origin of the event, resolves the update of the given type. */
def resolve[A]: A
/** Retrieves the immediate parents from the push phase. */
def parents(source: Event[T, Any]): Push.Parents[T]
/** Pulls the update from the given source. */
def apply[A](source: EventLike[T, A]): Option[A]
/** Whether the selector has been visited during the push phase. */
def contains(source: EventLike[T, Any]): Boolean
def isOrigin(source: EventLike[T, Any]): Boolean
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy