monifu.reactive.internals.builders.from.scala Maven / Gradle / Ivy
/*
* Copyright (c) 2014-2016 by its authors. Some rights reserved.
* See the project homepage at: https://monix.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package monifu.reactive.internals.builders
import java.util.concurrent.Callable
import monifu.reactive.Ack.{Cancel, Continue}
import monifu.reactive.{Subscriber, Ack, Observable}
import monifu.reactive.internals._
import scala.annotation.tailrec
import scala.concurrent.Future
import scala.util.{Try, Failure, Success}
import scala.util.control.NonFatal
private[reactive] object from {
/**
* Implementation for [[Observable.fromIterable]].
*/
def iterable[T](iterable: Iterable[T]): Observable[T] =
Observable.create { subscriber =>
var streamError = true
try {
val i = iterable.iterator
streamError = false
iterator(i).onSubscribe(subscriber)
}
catch {
case NonFatal(ex) if streamError =>
subscriber.onError(ex)
}
}
/**
* Implementation for [[Observable.fromIterator]].
*/
def iterator[T](iterator: Iterator[T]): Observable[T] = {
Observable.create { subscriber =>
import subscriber.{scheduler => s}
val modulus = s.env.batchSize - 1
def startFeedLoop(iterator: Iterator[T]): Unit = s.execute(new Runnable {
/**
* Loops synchronously, pushing elements into the downstream observer,
* until either the iterator is finished or an asynchronous barrier
* is being hit.
*/
@tailrec
def fastLoop(syncIndex: Int): Unit = {
// the result of onNext calls, on which we must do back-pressure
var ack: Future[Ack] = Continue
// we do not want to catch errors from our interaction with our observer,
// since SafeObserver should take care of than, hence we must only
// catch and stream errors related to the interactions with the iterator
var streamError = true
// true in case our iterator is seen to be empty and we must signal onComplete
var iteratorIsDone = false
// non-null in case we caught an iterator related error and we must signal onError
var iteratorTriggeredError: Throwable = null
try {
if (iterator.hasNext) {
val next = iterator.next()
streamError = false
ack = subscriber.onNext(next)
}
else
iteratorIsDone = true
}
catch {
case NonFatal(ex) if streamError =>
iteratorTriggeredError = ex
}
if (iteratorIsDone) {
subscriber.onComplete()
}
else if (iteratorTriggeredError != null) {
subscriber.onError(iteratorTriggeredError)
}
else {
val nextIndex = if (!ack.isCompleted) 0 else
(syncIndex + 1) & modulus
if (nextIndex > 0) {
if (ack == Continue)
fastLoop(nextIndex)
else ack.value.get match {
case Continue.IsSuccess =>
fastLoop(nextIndex)
case Failure(ex) =>
s.reportFailure(ex)
case _ =>
() // do nothing
}
}
else ack.onComplete {
case Continue.IsSuccess =>
run()
case Failure(ex) =>
s.reportFailure(ex)
case _ =>
() // do nothing
}
}
}
def run(): Unit = {
fastLoop(0)
}
})
var streamError = true
try {
val isEmpty = iterator.isEmpty
streamError = false
if (isEmpty)
subscriber.onComplete()
else
startFeedLoop(iterator)
}
catch {
case NonFatal(ex) if streamError =>
subscriber.onError(ex)
}
}
}
/**
* Implementation for [[Observable.fromFuture]].
*/
def future[T](f: Future[T]): Observable[T] =
Observable.create { subscriber =>
import subscriber.{scheduler => s}
f.value match {
case Some(Success(value)) =>
subscriber.onNext(value).onContinueSignalComplete(subscriber)
case Some(Failure(ex)) =>
subscriber.onError(ex)
case None => f.onComplete {
case Success(value) =>
subscriber.onNext(value).onContinueSignalComplete(subscriber)
case Failure(ex) =>
subscriber.onError(ex)
}
}
}
/**
* Implementation for [[Observable.fromTask]].
*/
def task[A](t: => A): Observable[A] =
Observable.create { subscriber =>
import subscriber.{scheduler => s}
s.execute {
try {
subscriber.onNext(t)
.onContinueSignalComplete(subscriber)
}
catch {
case NonFatal(ex) =>
try subscriber.onError(ex) catch {
case NonFatal(err) =>
s.reportFailure(ex)
s.reportFailure(err)
}
}
}
}
/**
* Implementation for [[Observable.fromCallable]].
*/
def callable[T](c: Callable[T]): Observable[T] =
Observable.create { subscriber =>
import subscriber.{scheduler => s}
s.execute {
try {
subscriber.onNext(c.call())
.onContinueSignalComplete(subscriber)
}
catch {
case NonFatal(ex) =>
try subscriber.onError(ex) catch {
case NonFatal(err) =>
s.reportFailure(ex)
s.reportFailure(err)
}
}
}
}
/**
* Implementation for [[Observable.fromRunable]].
*/
def runnable[T](r: Runnable): Observable[Unit] =
Observable.create { subscriber =>
import subscriber.{scheduler => s}
s.execute {
try {
subscriber.onNext(r.run())
.onContinueSignalComplete(subscriber)
}
catch {
case NonFatal(ex) =>
try subscriber.onError(ex) catch {
case NonFatal(err) =>
s.reportFailure(ex)
s.reportFailure(err)
}
}
}
}
/**
* Implementation for [[Observable.fromStateAction]].
*/
def stateAction[S,A](f: S => (A,S))(seed: S): Observable[A] =
Observable.create { subscriber =>
import subscriber.scheduler
scheduler.execute(new StateRunLoop(subscriber, seed, f))
}
private[this]
final class StateRunLoop[S,A](o: Subscriber[A], initialSeed: S, f: S => (A,S))
extends Runnable { self =>
import o.{scheduler => s}
private[this] var seed = initialSeed
private[this] val modulus = s.env.batchSize - 1
private[this] val asyncReschedule: Try[Ack] => Unit = {
case Continue.IsSuccess =>
self.run()
case Failure(ex) =>
o.onError(ex)
case _ =>
() // do nothing else
}
@tailrec
def fastLoop(syncIndex: Int): Unit = {
val ack = try {
val (nextA, newState) = f(seed)
this.seed = newState
o.onNext(nextA)
}
catch {
case NonFatal(ex) =>
o.onError(ex)
Cancel
}
val nextIndex = if (!ack.isCompleted) 0 else
(syncIndex + 1) & modulus
if (nextIndex > 0) {
if (ack == Continue)
fastLoop(nextIndex)
else ack.value.get match {
case Continue.IsSuccess =>
fastLoop(nextIndex)
case Failure(ex) =>
o.onError(ex)
case _ =>
() // do nothing else
}
}
else {
ack.onComplete(asyncReschedule)
}
}
def run(): Unit =
try fastLoop(0) catch {
case NonFatal(ex) =>
s.reportFailure(ex)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy