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

monix.execution.schedulers.SchedulerCompanionImpl.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 monix.execution.schedulers

import java.lang.Thread.UncaughtExceptionHandler
import java.util.concurrent.{Executors, ScheduledExecutorService}
import monix.execution.UncaughtExceptionReporter._
import monix.execution.{Scheduler, SchedulerCompanion, UncaughtExceptionReporter}
import monix.execution.internal.ForkJoinPool
import scala.concurrent.ExecutionContext

/** @define applyDesc The resulting [[Scheduler]] will piggyback on top of a Java
  *         `ScheduledExecutorService` for scheduling tasks for execution with
  *         a delay and a Scala `ExecutionContext` for actually executing the
  *         tasks.
  *
  * @define executorService is the `ScheduledExecutorService` that handles the
  *         scheduling of tasks into the future. If not provided, an internal
  *         default will be used. You can also create one with
  *         `java.util.concurrent.Executors`.
  *
  * @define executionContext is the execution context in which all tasks will run.
  *         Use `scala.concurrent.ExecutionContext.Implicits.global`
  *         for the default.
  *
  * @define executionModel is the preferred
  *         [[monix.execution.schedulers.ExecutionModel ExecutionModel]],
  *         a guideline for run-loops and producers of data. Use
  *         [[monix.execution.schedulers.ExecutionModel.Default ExecutionModel.Default]]
  *         for the default.
  *
  * @define reporter is the [[UncaughtExceptionReporter]] that logs uncaught exceptions.
  *         Wrap the given `ExecutionContext.reportFailure` or use
  *         [[UncaughtExceptionReporter.LogExceptionsToStandardErr]] for
  *         the default.
  */
private[execution] class SchedulerCompanionImpl extends SchedulerCompanion {
  /** [[monix.execution.Scheduler Scheduler]] builder.
    *
    * The resulting [[Scheduler]] will piggyback on top of a Java
    * `ScheduledExecutorService` for scheduling tasks for execution with
    * a delay and a Scala `ExecutionContext` for actually executing the
    * tasks.
    *
    * @param executor $executorService
    * @param ec $executionContext
    * @param reporter $reporter
    * @param executionModel $executionModel
    */
  def apply(
    executor: ScheduledExecutorService,
    ec: ExecutionContext,
    reporter: UncaughtExceptionReporter,
    executionModel: ExecutionModel): Scheduler =
    AsyncScheduler(executor, ec, reporter, executionModel)

  /** [[monix.execution.Scheduler Scheduler]] builder.
    *
    * @param executor $executorService
    * @param ec $executionContext
    */
  def apply(executor: ScheduledExecutorService, ec: ExecutionContext): Scheduler =
    AsyncScheduler(executor, ec, UncaughtExceptionReporter(ec.reportFailure), ExecutionModel.Default)

  /** [[monix.execution.Scheduler Scheduler]] builder.
    *
    * @param ec $executionContext
    * @param reporter $reporter
    */
  def apply(ec: ExecutionContext, reporter: UncaughtExceptionReporter): Scheduler =
    AsyncScheduler(DefaultScheduledExecutor, ec, reporter, ExecutionModel.Default)

  /** [[monix.execution.Scheduler Scheduler]] builder .
    *
    * @param ec $executionContext
    * @param reporter $reporter
    * @param executionModel $executionModel
    */
  def apply(ec: ExecutionContext, reporter: UncaughtExceptionReporter, executionModel: ExecutionModel): Scheduler =
    AsyncScheduler(DefaultScheduledExecutor, ec, reporter, executionModel)

  /** [[monix.execution.Scheduler Scheduler]] builder that converts a Java `ScheduledExecutorService` into
    * a scheduler.
    *
    * @param executor $executorService
    * @param reporter $reporter
    */
  def apply(executor: ScheduledExecutorService, reporter: UncaughtExceptionReporter): Scheduler =
    ExecutorScheduler(executor, reporter, ExecutionModel.Default)

  /** [[monix.execution.Scheduler Scheduler]] builder that converts a Java `ScheduledExecutorService` into
    * a scheduler.
    *
    * @param executor $executorService
    * @param reporter $reporter
    * @param executionModel $executionModel
    */
  def apply(executor: ScheduledExecutorService, reporter: UncaughtExceptionReporter, executionModel: ExecutionModel): Scheduler =
    ExecutorScheduler(executor, reporter, executionModel)

  /** [[monix.execution.Scheduler Scheduler]] builder that converts a Java `ScheduledExecutorService` into
    * a scheduler.
    *
    * @param executor $executorService
    */
  def apply(executor: ScheduledExecutorService): Scheduler =
    ExecutorScheduler(executor, LogExceptionsToStandardErr, ExecutionModel.Default)

  /** [[monix.execution.Scheduler Scheduler]] builder that converts a Java `ScheduledExecutorService` into
    * a scheduler.
    *
    * @param executor $executorService
    * @param executionModel $executionModel
    */
  def apply(executor: ScheduledExecutorService, executionModel: ExecutionModel): Scheduler =
    ExecutorScheduler(executor, LogExceptionsToStandardErr, executionModel)

  /** [[monix.execution.Scheduler Scheduler]] builder - uses monix's
    * default `ScheduledExecutorService` for handling the scheduling of tasks.
    *
    * @param ec is the execution context in which all tasks will run.
    */
  def apply(ec: ExecutionContext): Scheduler =
    AsyncScheduler(
      DefaultScheduledExecutor, ec,
      UncaughtExceptionReporter(ec.reportFailure),
      ExecutionModel.Default
    )

  /** [[monix.execution.Scheduler Scheduler]] builder - uses monix's
    * default `ScheduledExecutorService` for handling the scheduling of tasks.
    *
    * @param ec is the execution context in which all tasks will run.
    * @param executionModel $executionModel
    */
  def apply(ec: ExecutionContext, executionModel: ExecutionModel): Scheduler =
    AsyncScheduler(
      DefaultScheduledExecutor, ec,
      UncaughtExceptionReporter(ec.reportFailure),
      executionModel
    )

  /** [[monix.execution.Scheduler Scheduler]] builder - uses monix's
    * default `ScheduledExecutorService` for handling the scheduling of tasks.
    * Uses Scala's `s.c.ExecutionContext.global` for actual execution.
    *
    * @param reporter $reporter
    * @param executionModel $executionModel
    */
  def apply(reporter: UncaughtExceptionReporter, executionModel: ExecutionModel): Scheduler = {
    val ec = ExecutionContext.Implicits.global
    AsyncScheduler(DefaultScheduledExecutor, ec, reporter, executionModel)
  }

  /** [[monix.execution.Scheduler Scheduler]] builder - uses monix's
    * default `ScheduledExecutorService` for handling the scheduling of tasks.
    * Uses Scala's `s.c.ExecutionContext.global` for actual execution.
    *
    * @param executionModel $executionModel
    */
  def apply(executionModel: ExecutionModel): Scheduler = {
    val ec = ExecutionContext.Implicits.global
    AsyncScheduler(
      DefaultScheduledExecutor, ec,
      UncaughtExceptionReporter(ec.reportFailure),
      executionModel
    )
  }

  /** Creates a [[monix.execution.Scheduler Scheduler]] meant for computational heavy tasks.
    *
    * Characteristics:
    *
    * - backed by Scala's `ForkJoinPool` for the task execution, in async mode
    * - uses monix's default `ScheduledExecutorService` instance for scheduling
    * - all created threads are daemonic
    * - cooperates with Scala's `BlockContext`
    *
    * @param parallelism is the number of threads that can run in parallel
    * @param reporter $reporter
    * @param executionModel $executionModel
    */
  def computation(parallelism: Int,
    reporter: UncaughtExceptionReporter = LogExceptionsToStandardErr,
    executionModel: ExecutionModel = ExecutionModel.Default): Scheduler = {
    val exceptionHandler = new UncaughtExceptionHandler {
      def uncaughtException(t: Thread, e: Throwable) =
        reporter.reportFailure(e)
    }

    val pool = ForkJoinPool(
      parallelism,
      ForkJoinPool.defaultForkJoinWorkerThreadFactory,
      exceptionHandler,
      asyncMode = true
    )

    val context = ExecutionContext.fromExecutor(pool, reporter.reportFailure)
    AsyncScheduler(DefaultScheduledExecutor, context, reporter, executionModel)
  }

  /** Creates a [[monix.execution.Scheduler Scheduler]] meant for blocking I/O tasks.
    *
    * Characteristics:
    *
    * - backed by a cached `ThreadPool` executor with 60 seconds of keep-alive
    * - the maximum number of threads is unbounded, as recommended for blocking I/O
    * - uses monix's default `ScheduledExecutorService` instance for scheduling
    * - doesn't cooperate with Scala's `BlockContext` because it is unbounded
    *
    * @param name the created threads name prefix, for easy identification.
    * @param daemonic specifies whether the created threads should be daemonic
    *                 (non-daemonic threads are blocking the JVM process on exit).
    * @param reporter $reporter
    * @param executionModel $executionModel
    */
  def io(name: String = "monix-io", daemonic: Boolean = true,
    reporter: UncaughtExceptionReporter = LogExceptionsToStandardErr,
    executionModel: ExecutionModel = ExecutionModel.Default): Scheduler = {
    val threadFactory = ThreadFactoryBuilder(name, daemonic)

    val context = ExecutionContext.fromExecutor(
      Executors.newCachedThreadPool(threadFactory),
      reporter.reportFailure
    )

    AsyncScheduler(DefaultScheduledExecutor, context, reporter, executionModel)
  }

  /** Builds a [[monix.execution.Scheduler Scheduler]] that schedules and executes tasks on its own thread.
    *
    * Characteristics:
    *
    * - backed by a single-threaded `ScheduledExecutorService` that takes care
    *   of both scheduling tasks in the future and of executing tasks
    * - does not cooperate with Scala's `BlockingContext`, so tasks should not
    *   block on the result of other tasks scheduled to run on this same thread
    *
    * @param name is the name of the created thread, for easy identification
    * @param daemonic specifies whether the created thread should be daemonic
    *                 (non-daemonic threads are blocking the JVM process on exit)
    * @param reporter $reporter
    * @param executionModel $executionModel
    */
  def singleThread(name: String, daemonic: Boolean = true,
    reporter: UncaughtExceptionReporter = LogExceptionsToStandardErr,
    executionModel: ExecutionModel = ExecutionModel.Default): Scheduler = {

    val executor =
      Executors.newSingleThreadScheduledExecutor(ThreadFactoryBuilder(name, daemonic))

    val context = new ExecutionContext {
      def reportFailure(t: Throwable) = reporter.reportFailure(t)
      def execute(runnable: Runnable) = executor.execute(runnable)
    }

    AsyncScheduler(executor, context, reporter, executionModel)
  }

  /** Builds a [[monix.execution.Scheduler Scheduler]] with a fixed thread-pool.
    *
    * Characteristics:
    *
    * - backed by a fixed pool `ScheduledExecutorService` that takes care
    *   of both scheduling tasks in the future and of executing immediate tasks
    * - does not cooperate with Scala's `BlockingContext`, so tasks should not
    *   block on the result of other tasks scheduled to run on this same thread
    *
    * @param name the created threads name prefix, for easy identification.
    * @param daemonic specifies whether the created thread should be daemonic
    * @param reporter is the [[UncaughtExceptionReporter]] that logs uncaught exceptions.
    */
  def fixedPool(name: String, poolSize: Int, daemonic: Boolean = true,
    reporter: UncaughtExceptionReporter = LogExceptionsToStandardErr,
    executionModel: ExecutionModel = ExecutionModel.Default): Scheduler = {

    val executor =
      Executors.newScheduledThreadPool(poolSize, ThreadFactoryBuilder(name, daemonic))

    ExecutorScheduler(executor, reporter, executionModel)
  }

  /** The default `ScheduledExecutor` instance.
    *
    * Currently it's a single-threaded Java `ScheduledExecutorService`
    * used for scheduling delayed tasks for execution. But the actual
    * execution will not happen on this executor service. In general
    * you can just reuse this one for all your scheduling needs.
    */
  lazy val DefaultScheduledExecutor: ScheduledExecutorService =
    Executors.newSingleThreadScheduledExecutor(ThreadFactoryBuilder("monix-scheduler"))

  /** The explicit global `Scheduler`. Invoke `global` when you want to provide the global
    * `Scheduler` explicitly.
    *
    * The default `Scheduler` implementation is backed by a work-stealing thread pool, along
    * with a single-threaded `ScheduledExecutionContext` that does the scheduling. By default,
    * the thread pool uses a target number of worker threads equal to the number of
    * [[https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors-- available processors]].
    *
    * @return the global `Scheduler`
    */
  def global: Scheduler = Implicits.global

  object Implicits extends ImplicitsLike {
    /** A global [[monix.execution.Scheduler Scheduler]] instance, provided for convenience, piggy-backing
      * on top of Scala's own `concurrent.ExecutionContext.global`, which is a
      * `ForkJoinPool`.
      *
      * It can be tuned by setting the following JVM system properties:
      *
      * - "scala.concurrent.context.minThreads" an integer specifying the minimum
      *   number of active threads in the pool
      *
      * - "scala.concurrent.context.maxThreads" an integer specifying the maximum
      *   number of active threads in the pool
      *
      * - "scala.concurrent.context.numThreads" can be either an integer,
      *   specifying the parallelism directly or a string with the format "xNUM"
      *   (e.g. "x1.5") specifying the multiplication factor of the number of
      *   available processors (taken with `Runtime.availableProcessors`)
      *
      * The formula for calculating the parallelism in our pool is
      * `min(maxThreads, max(minThreads, numThreads))`.
      *
      * To set a system property from the command line, a JVM parameter must be
      * given to the `java` command as `-Dname=value`. So as an example, to customize
      * this global scheduler, we could start our process like this:
      *
      * 
      *   java -Dscala.concurrent.context.minThreads=2 \
      *        -Dscala.concurrent.context.maxThreads=30 \
      *        -Dscala.concurrent.context.numThreads=x1.5 \
      *        ...
      * 
* * As a note, this being backed by Scala's own global execution context, * it is cooperating with Scala's BlockContext, so when operations marked * with `scala.concurrent.blocking` are encountered, the thread-pool may * decide to add extra threads in the pool. However this is not a thread-pool * that is optimal for doing blocking operations, so for example if you want * to do a lot of blocking I/O, then use a Scheduler backed by a * thread-pool that is more optimal for blocking. See for example * [[io]]. */ implicit lazy val global: Scheduler = AsyncScheduler( DefaultScheduledExecutor, ExecutionContext.Implicits.global, UncaughtExceptionReporter.LogExceptionsToStandardErr, ExecutionModel.Default ) } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy