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

monix.reactive.internal.builders.Interleave2Observable.scala Maven / Gradle / Ivy

package monix.reactive.internal.builders

import monix.execution.Ack.{Stop, Continue}
import monix.execution.cancelables.CompositeCancelable
import monix.execution.{Ack, Cancelable}
import monix.reactive.Observable
import monix.reactive.observers.Subscriber
import scala.concurrent.{Future, Promise}

private[reactive] final class Interleave2Observable[+A]
  (obsA1: Observable[A], obsA2: Observable[A]) extends Observable[A] { self =>

  def unsafeSubscribeFn(out: Subscriber[A]): Cancelable = {
    import out.scheduler

    // MUST BE synchronized by `self`
    var isDone = false
    // MUST BE synchronized by `self`
    var downstreamAck = Continue : Future[Ack]
    // MUST BE synchronized by `self`.
    // This essentially serves as a lock for obsA1 when `select` is not assigned to it
    // pauseA1 is initialized to be `Continue`, so that obsA1 is deterministically emitted before obsA2
    var pauseA1 = Promise.successful(Continue : Ack)
    // This essentially serves as a lock for obsA2 when `select` is not assigned to it
    var pauseA2 = Promise[Ack]()

    // MUST BE synchronized by `self`
    var completedCount = 0
    var lastAck1 = Continue : Future[Ack]
    var lastAck2 = Continue : Future[Ack]

    def signalOnError(ex: Throwable): Unit =
      self.synchronized {
        if (!isDone) {
          isDone = true
          out.onError(ex)
          downstreamAck = Stop
          pauseA1.tryCompleteWith(Stop)
          pauseA2.tryCompleteWith(Stop)
        }
      }

    // MUST BE synchronized by `self`
    def signalOnComplete(ack: Future[Ack]): Unit = {
      @inline def rawOnComplete(): Unit =
        self.synchronized(if (!isDone) {
          isDone = true
          out.onComplete()
        })

      val shouldComplete = !isDone && {
        completedCount += 1
        completedCount >= 2
      }

      if (shouldComplete)
        ack.syncOnContinue(rawOnComplete())
    }

    val composite = CompositeCancelable()

    composite += obsA1.unsafeSubscribeFn(new Subscriber[A] {
      implicit val scheduler = out.scheduler

      def onNext(elem: A): Future[Ack] = self.synchronized {
        @inline def sendSignal(a: A): Future[Ack] = self.synchronized {
          if (isDone) Stop else {
            downstreamAck = out.onNext(a)
            pauseA1 = Promise[Ack]()
            pauseA2.tryCompleteWith(downstreamAck)
            downstreamAck
          }
        }

        // Pausing A1 until obsA2 allows us to send
        lastAck1 = pauseA1.future.syncTryFlatten.syncFlatMap {
          case Continue => sendSignal(elem)
          case Stop => Stop
        }

        lastAck1
      }

      def onError(ex: Throwable): Unit =
        signalOnError(ex)

      def onComplete(): Unit = self.synchronized {
        lastAck1.syncOnContinue {
          signalOnComplete(lastAck1)
          pauseA2.trySuccess(Continue)
          pauseA2 = Promise.successful(Continue)
        }
      }
    })

    composite += obsA2.unsafeSubscribeFn(new Subscriber[A] {
      implicit val scheduler = out.scheduler

      def onNext(elem: A): Future[Ack] = self.synchronized {
        @inline def sendSignal(a: A): Future[Ack] = self.synchronized {
          if (isDone) Stop else {
            downstreamAck = out.onNext(a)
            pauseA2 = Promise[Ack]()
            pauseA1.tryCompleteWith(downstreamAck)
            downstreamAck
          }
        }

        // Pausing A2 until obsA1 allows us to send
        lastAck2 = pauseA2.future.syncTryFlatten.syncFlatMap {
          case Continue => sendSignal(elem)
          case Stop => Stop
        }

        lastAck2
      }

      def onError(ex: Throwable): Unit =
        signalOnError(ex)

      def onComplete(): Unit = self.synchronized {
        lastAck2.syncOnContinue {
          signalOnComplete(lastAck2)
          pauseA1.trySuccess(Continue)
          pauseA1 = Promise.successful(Continue)
        }
      }
    })

    composite
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy