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

org.atnos.eff.ExecutorServices.scala Maven / Gradle / Ivy

The newest version!
package org.atnos.eff

import java.util.Collections
import java.util.concurrent._
import cats.Eval
import org.atnos.eff.concurrent.Scheduler
import org.atnos.eff.concurrent.Schedulers
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContextExecutorService
import scala.concurrent.Promise
import scala.util.Try

case class ExecutorServices(
  executorServiceEval: Eval[ExecutorService],
  scheduledExecutorEval: Eval[ScheduledExecutorService],
  executionContextEval: Eval[ExecutionContext]
) {

  /** note: shutdown only shuts down the executor services */
  def shutdown: Eval[Unit] = Eval.later {
    // careful: calling executorService.shutdown or scheduledExecutorService will deadlock!
    try executorServiceEval.value.shutdown()
    finally scheduledExecutorEval.value.shutdown()
  }

  implicit lazy val executorService: ExecutorService =
    executorServiceEval.value

  implicit lazy val scheduledExecutorService: ScheduledExecutorService =
    scheduledExecutorEval.value

  implicit lazy val executionContext: ExecutionContext =
    executionContextEval.value

  implicit lazy val scheduler: Scheduler =
    ExecutorServices.schedulerFromScheduledExecutorService(scheduledExecutorService)

  /** convenience method to shutdown the services when the final future has completed */
  def shutdownOnComplete[A](future: scala.concurrent.Future[A]): ExecutorServices = {
    future.onComplete(_ => shutdown.value)
    this
  }

}

object ExecutorServices extends Schedulers {

  lazy val threadsNb = Runtime.getRuntime.availableProcessors

  def create(implicit es: ExecutorService, s: ScheduledExecutorService): ExecutorServices =
    fromExecutorServices(es, s)

  def fromExecutorServices(es: => ExecutorService, s: => ScheduledExecutorService): ExecutorServices =
    ExecutorServices(
      Eval.later(es),
      Eval.later(s),
      Eval.later(createExecutionContext(es))
    )

  def fromExecutorService(es: => ExecutorService): ExecutorServices =
    fromExecutorServices(es, scheduledExecutor(threadsNb))

  def createExecutionContext(executorService: ExecutorService, logger: String => Unit = println): ExecutionContext =
    ExecutionContext.fromExecutorService(executorService, (t: Throwable) => logger(t.getStackTrace.mkString("\n")))

  def executor(threadsNb: Int): ExecutorService =
    Executors.newFixedThreadPool(threadsNb)

  def scheduledExecutor(scheduledThreadsNb: Int): ScheduledExecutorService =
    Executors.newScheduledThreadPool(scheduledThreadsNb)

  /**
   * create an ExecutionEnv from an execution context only
   *
   * WARNING!!! This method create a brand new scheduledExecutorService which will be used if
   * you use the ExecutorServices to timeout an Async effect
   */
  def fromExecutionContext(ec: => ExecutionContext): ExecutorServices =
    ExecutorServices(Eval.later(executorFromExecutionContext(ec)), Eval.later(scheduledExecutor(threadsNb)), Eval.later(ec))

  /** taken from [[https://gist.github.com/viktorklang/5245161]] */
  def executorFromExecutionContext(ec: => ExecutionContext): ExecutorService =
    ec match {
      case null => throw null
      case eces: ExecutionContextExecutorService => eces
      case other =>
        new AbstractExecutorService with ExecutionContextExecutorService {
          override def prepare(): ExecutionContext = other
          override def isShutdown = false
          override def isTerminated = false
          override def shutdown() = ()
          override def shutdownNow() = Collections.emptyList[Runnable]
          override def execute(runnable: Runnable): Unit = other execute runnable
          override def reportFailure(t: Throwable): Unit = other reportFailure t
          override def awaitTermination(length: Long, unit: TimeUnit): Boolean = false
        }
    }

  /** create an ExecutorServices from Scala global execution context */
  def fromGlobalExecutionContext: ExecutorServices =
    fromExecutionContext(scala.concurrent.ExecutionContext.global)

  /** create a Scheduler from Scala global execution context */
  def schedulerFromGlobalExecutionContext: Scheduler =
    schedulerFromScheduledExecutorService(fromGlobalExecutionContext.scheduledExecutorService)

  def schedulerFromScheduledExecutorService(s: ScheduledExecutorService): Scheduler =
    new Scheduler {
      def schedule(timedout: => Unit, duration: FiniteDuration): () => Unit = {
        val scheduled = s.schedule(new Runnable { def run(): Unit = timedout }, duration.toNanos, TimeUnit.NANOSECONDS)
        () => { scheduled.cancel(false); () }
      }

      def delay(duration: FiniteDuration): scala.concurrent.Future[Unit] = {
        val p = Promise[Unit]()
        s.schedule(new Runnable { def run(): Unit = { p.complete(Try(())); () } }, duration.toNanos, TimeUnit.NANOSECONDS)
        p.future
      }

      override def toString = "Scheduler"
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy