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

monifu.reactive.internals.package.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014 by its authors. Some rights reserved.
 * See the project homepage at
 *
 *     http://www.monifu.org/
 *
 * 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

import monifu.concurrent.Scheduler
import monifu.concurrent.atomic.AtomicBoolean
import monifu.reactive.Ack.{Cancel, Continue}

import scala.concurrent.{Future, Promise}
import scala.util.control.NonFatal
import scala.util.{Failure, Try}


package object internals {
  /**
   * Internal extensions to Future[Ack] used in the implementation of Observable.
   */
  implicit class FutureAckExtensions(val source: Future[Ack]) extends AnyVal {
    /**
     * On Continue, triggers the execution of the given callback.
     */
    def onContinue(cb: => Unit)(implicit s: Scheduler): Unit =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || (sync != Cancel && sync.value.get == Continue.IsSuccess))
            try cb catch {
              case NonFatal(ex) =>
                s.reportFailure(ex)
            }
        case async =>
          async.onSuccess {
            case Continue => cb
          }
      }

    def onContinueComplete[T](observer: Observer[T], ex: Throwable = null)(implicit s: Scheduler): Unit =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || ((sync != Cancel) && sync.value.get == Continue.IsSuccess)) {
            var streamError = true
            try {
              if (ex eq null)
                observer.onComplete()
              else {
                streamError = false
                observer.onError(ex)
              }
            }
            catch {
              case NonFatal(err) =>
                if (streamError)
                  observer.onError(err)
                else
                  s.reportFailure(err)
            }
          }
        case async =>
          async.onSuccess {
            case Continue =>
              var streamError = true
              try {
                if (ex eq null)
                  observer.onComplete()
                else {
                  streamError = false
                  observer.onError(ex)
                }
              }
              catch {
                case NonFatal(err) =>
                  if (streamError)
                    observer.onError(err)
                  else
                    s.reportFailure(err)
              }
          }
      }

    def onContinueStreamOnNext[T](observer: Observer[T], nextElem: T)(implicit s: Scheduler) =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || sync.value.get == Continue.IsSuccess)
            observer.onNext(nextElem)
          else
            Cancel
        case async =>
          async.flatMap {
            case Continue =>
              observer.onNext(nextElem)
            case Cancel =>
              Cancel
          }
      }

    def onContinueCompleteWith[T](observer: Observer[T], lastElem: T)(implicit s: Scheduler): Unit =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || ((sync != Cancel) && sync.value.get == Continue.IsSuccess)) {
            try {
              observer.onNext(lastElem)
              observer.onComplete()
            }
            catch {
              case NonFatal(err) =>
                observer.onError(err)
            }
          }
        case async =>
          async.onSuccess {
            case Continue =>
              try {
                observer.onNext(lastElem)
                observer.onComplete()
              }
              catch {
                case NonFatal(err) =>
                  observer.onError(err)
              }
          }
      }

    /**
     * On Cancel, triggers Continue on the given Promise.
     */
    def onCancelContinue(p: Promise[Ack])(implicit s: Scheduler): Future[Ack] = {
      source match {
        case Continue => // do nothing
        case Cancel => p.success(Continue)

        case sync if sync.isCompleted && sync.value.get.isSuccess =>
          sync.value.get.get match {
            case Continue => // do nothing
            case Cancel => p.success(Continue)
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess => // nothing
            case Cancel.IsSuccess => p.success(Continue)
            case Failure(ex) => p.failure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }
      }
      source
    }

    /**
     * On Cancel, try to trigger Cancel on the given Promise.
     */
    def ifCancelTryCanceling(p: Promise[Ack])(implicit s: Scheduler): Future[Ack] = {
      source match {
        case Continue => // do nothing
        case Cancel => p.trySuccess(Cancel)

        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess => // do nothing
            case Cancel.IsSuccess => p.trySuccess(Cancel)
            case Failure(ex) => p.tryFailure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess => // nothing
            case Cancel.IsSuccess => p.trySuccess(Cancel)
            case Failure(ex) => p.tryFailure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }
      }
      source
    }

    /**
     * On Cancel, try to trigger Cancel on the given Promise.
     */
    def ifCanceledDoCancel(p: Promise[Ack])(implicit s: Scheduler): Future[Ack] = {
      source match {
        case Continue => // do nothing
        case Cancel => p.success(Cancel)

        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess => // do nothing
            case Cancel.IsSuccess => p.success(Cancel)
            case Failure(ex) => p.failure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess => // nothing
            case Cancel.IsSuccess => p.success(Cancel)
            case Failure(ex) => p.failure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }
      }
      source
    }

    /**
     * Unsafe version of `onComplete` that triggers execution synchronously
     * in case the source is already completed.
     */
    def onCompleteNow(f: Try[Ack] => Unit)(implicit s: Scheduler): Future[Ack] =
      source match {
        case sync if sync.isCompleted =>
          try f(sync.value.get) catch {
            case NonFatal(ex) =>
              s.reportFailure(ex)
          }
          source
        case async =>
          source.onComplete(f)
          source
      }

    /**
     * Triggers execution of the given callback, once the source terminates either
     * with a `Cancel` or with a failure.
     */
    def onCancel(cb: => Unit)(implicit s: Scheduler): Future[Ack] =
      source match {
        case Continue => source
        case Cancel =>
          try cb catch { case NonFatal(ex) => s.reportFailure(ex) }
          source
        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess => source
            case Cancel.IsSuccess | Failure(_) =>
              try cb catch { case NonFatal(ex) => s.reportFailure(ex) }
              source
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
              source
          }
        case async =>
          source.onComplete {
            case Cancel.IsSuccess | Failure(_) => cb
            case _ => // nothing
          }
          source
      }

    /**
     * Utility used in [[monifu.reactive.observers.SafeObserver.onNext]] for
     * handling errors in `onNext`. Avoids submitting tasks to the pool in case the
     * future is already complete and we can thus determine if it's a failure,
     * in which case we only need to cancel the stream and report the error.
     */
    def onErrorCancelStream[T](downstream: Observer[T], isDone: AtomicBoolean)
        (implicit scheduler: Scheduler): Future[Ack] = {

      def report(ex: Throwable) = {
        if (isDone.compareAndSet(expect=false, update=true)) {
          try downstream.onError(ex) catch {
            case NonFatal(oops) =>
              scheduler.reportFailure(oops)
          }

          Cancel
        }
        else {
          scheduler.reportFailure(ex)
          Cancel
        }
      }

      source match {
        case sync if sync.isCompleted =>
          if (sync.value.get.isFailure)
            report(sync.value.get.failed.get)
          else
            sync

        case async =>
          source.recover { case ex => report(ex) }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy