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

kyo.concurrent.scheduler.IOPromise.scala Maven / Gradle / Ivy

There is a newer version: 0.8.5
Show newest version
package kyo.concurrent.scheduler

import kyo._
import kyo.concurrent.fibers.Fibers
import kyo.ios._
import kyo.logs._

import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.locks.AbstractQueuedSynchronizer
import scala.annotation.tailrec
import scala.util.control.NonFatal

import IOPromise._
import java.util.concurrent.locks.LockSupport
import java.util.concurrent.locks.Lock

private[kyo] class IOPromise[T](state: State[T])
    extends AtomicReference(state) {

  private implicit def flat[S]: Flat[T > S] =
    Flat.unsafe.checked

  def this() = this(Pending())

  final def isDone(): Boolean = {
    @tailrec def loop(promise: IOPromise[T]): Boolean =
      promise.get() match {
        case p: Pending[T] @unchecked =>
          false
        case l: Linked[T] @unchecked =>
          loop(l.p)
        case _ =>
          true
      }
    loop(this)
  }

  final def interrupts(p: IOPromise[_]): Unit =
    onComplete { _ =>
      p.interrupt()
    }

  final def interrupt(): Boolean = {
    @tailrec def loop(promise: IOPromise[T]): Boolean =
      promise.get() match {
        case p: Pending[T] @unchecked =>
          promise.complete(p, Fibers.interrupted) || loop(promise)
        case l: Linked[T] @unchecked =>
          loop(l.p)
        case _ =>
          false
      }
    loop(this)
  }

  private def compress(): IOPromise[T] = {
    @tailrec def loop(p: IOPromise[T]): IOPromise[T] =
      p.get() match {
        case l: Linked[T] @unchecked =>
          loop(l.p)
        case _ =>
          p
      }
    loop(this)
  }

  private def merge(p: Pending[T]): Unit = {
    @tailrec def loop(promise: IOPromise[T]): Unit =
      promise.get() match {
        case p2: Pending[T] @unchecked =>
          if (!promise.compareAndSet(p2, p2.merge(p)))
            loop(promise)
        case l: Linked[T] @unchecked =>
          loop(l.p)
        case v =>
          p.flush(v.asInstanceOf[T > IOs])
      }
    loop(this)
  }

  final def become(other: IOPromise[T]): Boolean = {
    @tailrec def loop(other: IOPromise[T]): Boolean =
      get() match {
        case p: Pending[T] @unchecked =>
          if (compareAndSet(p, Linked(other))) {
            other.merge(p)
            true
          } else {
            loop(other)
          }
        case _ =>
          false
      }
    loop(other.compress())
  }

  final def onComplete(f: T > IOs => Unit): Unit = {
    @tailrec def loop(promise: IOPromise[T]): Unit =
      promise.get() match {
        case p: Pending[T] @unchecked =>
          if (!promise.compareAndSet(p, p.add(f)))
            loop(promise)
        case l: Linked[T] @unchecked =>
          loop(l.p)
        case v =>
          try f(v.asInstanceOf[T > IOs])
          catch {
            case ex if NonFatal(ex) =>
              Logs.unsafe.error("uncaught exception", ex)
          }
      }
    loop(this)
  }

  protected def onComplete(): Unit = {}

  private def complete(p: Pending[T], v: T > IOs): Boolean =
    compareAndSet(p, v) && {
      onComplete()
      p.flush(v)
      true
    }

  final def complete(v: T > IOs): Boolean = {
    @tailrec def loop(): Boolean =
      get() match {
        case p: Pending[T] @unchecked =>
          complete(p, v) || loop()
        case _ =>
          false
      }
    loop()
  }

  final def block(): T = {
    def loop(promise: IOPromise[T]): T =
      promise.get() match {
        case _: Pending[T] @unchecked =>
          val b = new (T > IOs => Unit) with (() => T > IOs) {
            @volatile
            private var result: T > IOs = null.asInstanceOf[T > IOs]
            private val waiter          = Thread.currentThread()
            def apply(v: T > IOs) = {
              result = v
              LockSupport.unpark(waiter)
            }
            def apply() = {
              while (result == null) {
                LockSupport.park()
              }
              result
            }
          }
          onComplete(b)
          IOs.run(b())
        case l: Linked[T] @unchecked =>
          loop(l.p)
        case v =>
          v.asInstanceOf[T]
      }
    Scheduler.flush()
    loop(this)
  }
}

private[kyo] object IOPromise {

  type State[T] = Any // (T > IOs) | Pending[T] | Linked[T]

  case class Linked[T](p: IOPromise[T])

  abstract class Pending[T] { self =>

    def run(v: T > IOs): Pending[T]

    def add(f: T > IOs => Unit): Pending[T] =
      new Pending[T] {
        def run(v: T > IOs) = {
          try f(v)
          catch {
            case ex if NonFatal(ex) =>
              Logs.unsafe.error("uncaught exception", ex)
          }
          self
        }
      }

    final def merge(tail: Pending[T]): Pending[T] = {
      @tailrec def loop(p: Pending[T], v: T > IOs): Pending[T] =
        p match {
          case _ if (p eq Pending.Empty) =>
            tail
          case p: Pending[T] =>
            loop(p.run(v), v)
        }
      new Pending[T] {
        def run(v: T > IOs) =
          loop(self, v)
      }
    }

    final def flush(v: T > IOs): Unit = {
      @tailrec def loop(p: Pending[T]): Unit =
        p match {
          case _ if (p eq Pending.Empty) => ()
          case p: Pending[T] =>
            loop(p.run(v))
        }
      loop(this)
    }
  }

  object Pending {
    def apply[T](): Pending[T] = Empty.asInstanceOf[Pending[T]]
    case object Empty extends Pending[Nothing] {
      def run(v: Nothing > IOs) = this
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy