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

monix.execution.Scheduler.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014-2019 by The Monix Project Developers.
 * 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

import java.util.concurrent.Executor
import monix.execution.internal.RunnableAction
import monix.execution.schedulers.SchedulerCompanionImpl
import scala.annotation.implicitNotFound
import scala.concurrent.ExecutionContext
import scala.concurrent.duration.{FiniteDuration, MILLISECONDS, TimeUnit}

/** A Scheduler is an `scala.concurrent.ExecutionContext` that additionally can
  * schedule the execution of units of work to run with a delay or periodically.
  */
@implicitNotFound(
  "Cannot find an implicit Scheduler, either " +
    "import monix.execution.Scheduler.Implicits.global or use a custom one")
trait Scheduler extends ExecutionContext with UncaughtExceptionReporter with Executor {
  /** Schedules the given `command` for execution at some time in the future.
    *
    * The command may execute in a new thread, in a pooled thread,
    * in the calling thread, basically at the discretion of the
    * [[Scheduler]] implementation.
    */
  def execute(command: Runnable): Unit

  /** Reports that an asynchronous computation failed. */
  def reportFailure(t: Throwable): Unit

  /** Schedules a task to run in the future, after `initialDelay`.
    *
    * For example the following schedules a message to be printed to
    * standard output after 5 minutes:
    * {{{
    *   val task = scheduler.scheduleOnce(5, TimeUnit.MINUTES, new Runnable {
    *     def run() = print("Hello, world!")
    *   })
    *
    *   // later if you change your mind ...
    *   task.cancel()
    * }}}
    *
    * @param initialDelay is the time to wait until the execution happens
    * @param unit is the time unit used for `initialDelay`
    * @param r is the callback to be executed
    * @return a `Cancelable` that can be used to cancel the created task
    *         before execution.
    */
  def scheduleOnce(initialDelay: Long, unit: TimeUnit, r: Runnable): Cancelable

  /** Schedules for execution a periodic task that is first executed
    * after the given initial delay and subsequently with the given
    * delay between the termination of one execution and the
    * commencement of the next.
    *
    * For example the following schedules a message to be printed to
    * standard output every 10 seconds with an initial delay of 5
    * seconds:
    * {{{
    *   val task = s.scheduleWithFixedDelay(5, 10, TimeUnit.SECONDS, new Runnable {
    *     def run() = print("Repeated message")
    *   })
    *
    *   // later if you change your mind ...
    *   task.cancel()
    * }}}
    *
    * @param initialDelay is the time to wait until the first execution happens
    * @param delay is the time to wait between 2 successive executions of the task
    * @param unit is the time unit used for the `initialDelay` and the `delay` parameters
    * @param r is the callback to be executed
    * @return a cancelable that can be used to cancel the execution of
    *         this repeated task at any time.
    */
  def scheduleWithFixedDelay(initialDelay: Long, delay: Long, unit: TimeUnit, r: Runnable): Cancelable

  /** Schedules a periodic task that becomes enabled first after the given
    * initial delay, and subsequently with the given period. Executions will
    * commence after `initialDelay` then `initialDelay + period`, then
    * `initialDelay + 2 * period` and so on.
    *
    * If any execution of the task encounters an exception, subsequent executions
    * are suppressed. Otherwise, the task will only terminate via cancellation or
    * termination of the scheduler. If any execution of this task takes longer
    * than its period, then subsequent executions may start late, but will not
    * concurrently execute.
    *
    * For example the following schedules a message to be printed to standard
    * output approximately every 10 seconds with an initial delay of 5 seconds:
    * {{{
    *   val task = scheduler.scheduleAtFixedRate(5, 10, TimeUnit.SECONDS, new Runnable {
    *     def run() = print("Repeated message")
    *   })
    *
    *   // later if you change your mind ...
    *   task.cancel()
    * }}}
    *
    * @param initialDelay is the time to wait until the first execution happens
    * @param period is the time to wait between 2 successive executions of the task
    * @param unit is the time unit used for the `initialDelay` and the `period` parameters
    * @param r is the callback to be executed
    * @return a cancelable that can be used to cancel the execution of
    *         this repeated task at any time.
    */
  def scheduleAtFixedRate(initialDelay: Long, period: Long, unit: TimeUnit, r: Runnable): Cancelable

  /** Returns the current time, as a Unix timestamp (number of time units
    * since the Unix epoch).
    *
    * This is the equivalent of Java's `System.currentTimeMillis`,
    * or of `CLOCK_REALTIME` from Linux's `clock_gettime()`.
    *
    * The provided `TimeUnit` determines the time unit of the output,
    * its precision, but not necessarily its resolution, which is
    * implementation dependent. For example this will return the number
    * of milliseconds since the epoch:
    *
    * {{{
    *   import scala.concurrent.duration.MILLISECONDS
    *
    *   scheduler.clockRealTime(MILLISECONDS)
    * }}}
    *
    * N.B. the resolution is limited by the underlying implementation
    * and by the underlying CPU and OS. If the implementation uses
    * `System.currentTimeMillis`, then it can't have a better
    * resolution than 1 millisecond, plus depending on underlying
    * runtime (e.g. Node.js) it might return multiples of 10
    * milliseconds or more.
    *
    * See [[clockMonotonic]], for fetching a monotonic value that
    * may be better suited for doing time measurements.
    */
  def clockRealTime(unit: TimeUnit): Long

  /**
    * Returns a monotonic clock measurement, if supported by the
    * underlying platform.
    *
    * This is the pure equivalent of Java's `System.nanoTime`,
    * or of `CLOCK_MONOTONIC` from Linux's `clock_gettime()`.
    *
    * {{{
    *   timer.clockMonotonic(NANOSECONDS)
    * }}}
    *
    * The returned value can have nanoseconds resolution and represents
    * the number of time units elapsed since some fixed but arbitrary
    * origin time. Usually this is the Unix epoch, but that's not
    * a guarantee, as due to the limits of `Long` this will overflow in
    * the future (2^63^ is about 292 years in nanoseconds) and the
    * implementation reserves the right to change the origin.
    *
    * The return value should not be considered related to wall-clock
    * time, the primary use-case being to take time measurements and
    * compute differences between such values, for example in order to
    * measure the time it took to execute a task.
    *
    * As a matter of implementation detail, Monix's `Scheduler`
    * implementations use `System.nanoTime` and the JVM will use
    * `CLOCK_MONOTONIC` when available, instead of `CLOCK_REALTIME`
    * (see `clock_gettime()` on Linux) and it is up to the underlying
    * platform to implement it correctly.
    *
    * And be warned, there are platforms that don't have a correct
    * implementation of `CLOCK_MONOTONIC`. For example at the moment of
    * writing there is no standard way for such a clock on top of
    * JavaScript and the situation isn't so clear cut for the JVM
    * either, see:
    *
    *  - [[https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6458294 bug report]]
    *  - [[http://cs.oswego.edu/pipermail/concurrency-interest/2012-January/008793.html concurrency-interest]]
    *    discussion on the X86 tsc register
    *
    * The JVM tries to do the right thing and at worst the resolution
    * and behavior will be that of `System.currentTimeMillis`.
    *
    * The recommendation is to use this monotonic clock when doing
    * measurements of execution time, or if you value monotonically
    * increasing values more than a correspondence to wall-time, or
    * otherwise prefer [[clockRealTime]].
    */
  def clockMonotonic(unit: TimeUnit): Long

  /** The [[ExecutionModel]] is a specification of how run-loops
    * and producers should behave in regards to executing tasks
    * either synchronously or asynchronously.
    */
  def executionModel: ExecutionModel

  /** Given a function that will receive the underlying
    * [[monix.execution.ExecutionModel ExecutionModel]],
    * returns a new [[Scheduler]] reference, based on the source,
    * that exposes the transformed `ExecutionModel`
    * when queried by means of the [[executionModel]] property.
    *
    * This method enables reusing global scheduler references in
    * a local scope, but with a slightly modified
    * [[monix.execution.ExecutionModel execution model]]
    * to inject.
    *
    * The contract of this method (things you can rely on):
    *
    *  1. the source `Scheduler` must not be modified in any way
    *  1. the implementation should wrap the source efficiently, such that the
    *     result mirrors the source `Scheduler` in every way except for
    *     the execution model
    *
    * Sample:
    * {{{
    *   import monix.execution.Scheduler.global
    *
    *   implicit val scheduler = {
    *     val em = global.executionModel
    *     global.withExecutionModel(em.withAutoCancelableLoops(true))
    *   }
    * }}}
    */
  def withExecutionModel(em: ExecutionModel): Scheduler

  def withUncaughtExceptionReporter(r: UncaughtExceptionReporter): Scheduler
}

private[monix] trait SchedulerCompanion {
  trait ImplicitsLike {
    def global: Scheduler
    def traced: Scheduler
  }

  def Implicits: ImplicitsLike
  def global: Scheduler
  def traced: Scheduler
}

object Scheduler extends SchedulerCompanionImpl {
  self: SchedulerCompanion =>

  /** Utilities complementing the `Scheduler` interface. */
  implicit final class Extensions(val source: Scheduler) extends AnyVal with schedulers.ExecuteExtensions {
    /** Schedules a task to run in the future, after `initialDelay`.
      *
      * For example the following schedules a message to be printed to
      * standard output after 5 minutes:
      * {{{
      *   val task = scheduler.scheduleOnce(5.minutes) {
      *     print("Hello, world!")
      *   }
      *
      *   // later, if you change your mind ...
      *   task.cancel()
      * }}}
      *
      * @param initialDelay is the time to wait until the execution happens
      * @param action is the callback to be executed
      * @return a `Cancelable` that can be used to cancel the created task
      *         before execution.
      */
    def scheduleOnce(initialDelay: FiniteDuration)(action: => Unit): Cancelable =
      source.scheduleOnce(initialDelay.length, initialDelay.unit, RunnableAction(action))

    /** Schedules for execution a periodic task that is first executed
      * after the given initial delay and subsequently with the given
      * delay between the termination of one execution and the
      * commencement of the next.
      *
      * For example the following schedules a message to be printed to
      * standard output every 10 seconds with an initial delay of 5
      * seconds:
      * {{{
      *   val task = s.scheduleWithFixedDelay(5.seconds, 10.seconds) {
      *     print("Repeated message")
      *   }
      *
      *   // later if you change your mind ...
      *   task.cancel()
      * }}}
      *
      * @param initialDelay is the time to wait until the first execution happens
      * @param delay is the time to wait between 2 successive executions of the task
      * @param action is the callback to be executed
      * @return a cancelable that can be used to cancel the execution of
      *         this repeated task at any time.
      */
    def scheduleWithFixedDelay(initialDelay: FiniteDuration, delay: FiniteDuration)(action: => Unit): Cancelable = {

      source.scheduleWithFixedDelay(initialDelay.toMillis, delay.toMillis, MILLISECONDS, RunnableAction(action))
    }

    /** Schedules a periodic task that becomes enabled first after the given
      * initial delay, and subsequently with the given period. Executions will
      * commence after `initialDelay` then `initialDelay + period`, then
      * `initialDelay + 2 * period` and so on.
      *
      * If any execution of the task encounters an exception, subsequent executions
      * are suppressed. Otherwise, the task will only terminate via cancellation or
      * termination of the scheduler. If any execution of this task takes longer
      * than its period, then subsequent executions may start late, but will not
      * concurrently execute.
      *
      * For example the following schedules a message to be printed to standard
      * output approximately every 10 seconds with an initial delay of 5 seconds:
      * {{{
      *   val task = scheduler.scheduleAtFixedRate(5.seconds, 10.seconds) {
      *     print("Repeated message")
      *   }
      *
      *   // later if you change your mind ...
      *   task.cancel()
      * }}}
      *
      * @param initialDelay is the time to wait until the first execution happens
      * @param period is the time to wait between 2 successive executions of the task
      * @param action is the callback to be executed
      * @return a cancelable that can be used to cancel the execution of
      *         this repeated task at any time.
      */
    def scheduleAtFixedRate(initialDelay: FiniteDuration, period: FiniteDuration)(action: => Unit): Cancelable = {

      source.scheduleAtFixedRate(initialDelay.toMillis, period.toMillis, MILLISECONDS, RunnableAction(action))
    }

    /** DEPRECATED — use [[Scheduler.clockRealTime clockRealTime(MILLISECONDS)]]. */
    @deprecated("Use clockRealTime(MILLISECONDS)", "3.0.0")
    def currentTimeMillis(): Long = {
      // $COVERAGE-OFF$
      source.clockRealTime(MILLISECONDS)
      // $COVERAGE-ON$
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy