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

io.reactors.protocol.MultiValve.scala Maven / Gradle / Ivy

The newest version!
package io.reactors.protocol



import io.reactors.Arrayable
import io.reactors.RCell
import io.reactors.Reactor
import io.reactors.Subscription
import io.reactors.container.RHashSet
import io.reactors.container.RRing



class MultiValve[T: Arrayable](val window: Int) {
  private val ring = new RRing[T](window)
  private val valves = new RHashSet[(Valve[T], RCell[Long])]
  private val slowest = valves.map(_._2).toSignalAggregate(Long.MaxValue)(math.min)
  private var oldest = 0L
  private val next = RCell(0L)
  private val flush = Reactor.self.system.channels.daemon.open[Unit]
  flush.events on {
    val total = slowest() - oldest
    oldest += total
    ring.dequeueMany(total.toInt)
  }

  val out: Valve[T] = {
    val c = Reactor.self.system.channels.daemon.shortcut.open[T]
    val forwarding = c.events onEvent { x =>
      if (ring.available()) {
        ring.enqueue(x)
        next := next() + 1
        if (slowest() > next()) ring.dequeue()
      } else throw new IllegalStateException("Valve is not available.")
    }
    Valve(
      c.channel,
      ring.available,
      forwarding.andThen(c.seal()).andThen(valves.clear()).andThen(flush.seal())
    )
  }

  def +=(v: Valve[T]): Subscription = {
    val pos = RCell(math.min(slowest(), next()))
    valves += (v, pos)

    val morePending = (pos zip next)(_ < _).changes.toSignal(pos() < next())
    val available = (v.available zip morePending)(_ && _)
    val moving = available.is(true) on {
      while (available()) {
        val idx = (pos() - oldest).toInt
        val x = ring(idx)
        v.channel ! x
        pos := pos() + 1
      }
      val total = slowest() - oldest
      if (total > 0) flush.channel ! ()
    }

    moving.chain(available).chain(morePending).andThen(valves -= (v, pos))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy