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

com.wavesplatform.utils.Schedulers.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform.utils

import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy
import java.util.concurrent.{Future as JavaFuture, *}

import io.netty.util.{Timeout, Timer}
import monix.execution.schedulers.{ExecutorScheduler, SchedulerService}
import monix.execution.{ExecutionModel, Features, UncaughtExceptionReporter}

import scala.concurrent.duration.*

/** Helper methods to create schedulers with custom DiscardPolicy */
object Schedulers {
  private abstract class AdaptedThreadPoolExecutor(
      corePoolSize: Int,
      factory: ThreadFactory,
      rejectedExecutionHandler: RejectedExecutionHandler
  ) extends ScheduledThreadPoolExecutor(corePoolSize, factory, rejectedExecutionHandler) {
    def reportFailure(t: Throwable): Unit

    override def afterExecute(r: Runnable, t: Throwable): Unit = {
      super.afterExecute(r, t)
      var exception: Throwable = t

      if ((exception eq null) && r.isInstanceOf[JavaFuture[_]]) {
        try {
          val future = r.asInstanceOf[JavaFuture[_]]
          if (future.isDone) future.get()
        } catch {
          case ex: ExecutionException =>
            exception = ex.getCause
          case _: InterruptedException =>
            // ignore/reset
            Thread.currentThread().interrupt()
          case _: CancellationException =>
            () // ignore
        }
      }

      if (exception ne null) reportFailure(exception)
    }
  }

  private[this] def threadFactory(name: String, daemonic: Boolean, reporter: UncaughtExceptionReporter): ThreadFactory = { (r: Runnable) =>
    val thread = new Thread(r)
    thread.setName(name + "-" + thread.getId)
    thread.setDaemon(daemonic)
    thread.setUncaughtExceptionHandler((_: Thread, e: Throwable) => reporter.reportFailure(e))

    thread
  }

  def singleThread(
      name: String,
      reporter: UncaughtExceptionReporter = UncaughtExceptionReporter.default,
      executionModel: ExecutionModel = ExecutionModel.Default,
      rejectedExecutionHandler: RejectedExecutionHandler = new DiscardOldestPolicy
  ): SchedulerService = {
    val factory = threadFactory(name, daemonic = true, reporter)
    val executor = new AdaptedThreadPoolExecutor(1, factory, rejectedExecutionHandler) {
      override def reportFailure(t: Throwable): Unit = reporter.reportFailure(t)
    }

    ExecutorScheduler(executor, reporter, executionModel, Features.empty)
  }

  def fixedPool(
      poolSize: Int,
      name: String,
      reporter: UncaughtExceptionReporter = UncaughtExceptionReporter.default,
      executionModel: ExecutionModel = ExecutionModel.Default,
      rejectedExecutionHandler: RejectedExecutionHandler = new DiscardOldestPolicy
  ): SchedulerService = {
    val factory = threadFactory(name, daemonic = true, reporter)
    val executor = new AdaptedThreadPoolExecutor(poolSize, factory, rejectedExecutionHandler) {
      override def reportFailure(t: Throwable): Unit = reporter.reportFailure(t)
    }

    ExecutorScheduler(executor, reporter, executionModel, Features.empty)
  }

  private class TimedWrapper[V](timer: Timer, timeout: FiniteDuration, delegate: RunnableScheduledFuture[V]) extends RunnableScheduledFuture[V] {
    @volatile
    private[this] var maybeScheduledTimeout     = Option.empty[Timeout]
    override def isPeriodic: Boolean            = delegate.isPeriodic
    override def getDelay(unit: TimeUnit): Long = delegate.getDelay(unit)
    override def compareTo(o: Delayed): Int     = delegate.compareTo(o)
    override def run(): Unit = {
      val workerThread = Thread.currentThread()
      maybeScheduledTimeout = Some(
        timer.newTimeout(
          (t: Timeout) =>
            if (!t.isCancelled) {
              workerThread.interrupt()
            },
          timeout.toMillis,
          MILLISECONDS
        )
      )
      delegate.run()
      maybeScheduledTimeout.foreach(_.cancel())
      maybeScheduledTimeout = None
    }
    override def cancel(mayInterruptIfRunning: Boolean): Boolean = {
      maybeScheduledTimeout.foreach(_.cancel())
      delegate.cancel(mayInterruptIfRunning)
    }
    override def isCancelled: Boolean                  = delegate.isCancelled
    override def isDone: Boolean                       = delegate.isDone
    override def get(): V                              = delegate.get()
    override def get(timeout: Long, unit: TimeUnit): V = delegate.get(timeout, unit)
  }

  def timeBoundedFixedPool(
      timer: Timer,
      timeout: FiniteDuration,
      poolSize: Int,
      name: String,
      reporter: UncaughtExceptionReporter = UncaughtExceptionReporter.default,
      executionModel: ExecutionModel = ExecutionModel.Default,
      rejectedExecutionHandler: RejectedExecutionHandler = new DiscardOldestPolicy
  ): SchedulerService = {
    val factory = threadFactory(name, daemonic = true, reporter)
    val executor = new AdaptedThreadPoolExecutor(poolSize, factory, rejectedExecutionHandler) {
      override def reportFailure(t: Throwable): Unit = reporter.reportFailure(t)
      override def decorateTask[V](runnable: Runnable, task: RunnableScheduledFuture[V]): RunnableScheduledFuture[V] =
        new TimedWrapper(timer, timeout, task)
      override def decorateTask[V](callable: Callable[V], task: RunnableScheduledFuture[V]): RunnableScheduledFuture[V] =
        new TimedWrapper(timer, timeout, task)
    }

    ExecutorScheduler(executor, reporter, executionModel, Features.empty)
  }

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

  implicit class ExecutorExt(val executor: Executor) extends AnyVal {
    // Catches InterruptedException correctly
    def executeCatchingInterruptedException[T](f: => T): Future[T] = {
      val promise = Promise[T]()
      executor.execute { () =>
        try promise.success(f)
        catch { case e @ (NonFatal(_) | _: InterruptedException) => promise.failure(e) }
      }
      promise.future
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy