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

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

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

import kyo._
import kyo.ios._

import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.LongAdder
import java.util.concurrent.locks.LockSupport
import scala.annotation.tailrec
import org.jctools.queues.MpmcUnboundedXaddArrayQueue

private[kyo] object Scheduler {

  private val coreWorkers = Flag(
      "coreWorkers",
      Math.ceil(Runtime.getRuntime().availableProcessors().toDouble / 2).intValue()
  )

  @volatile
  private var concurrencyLimit = coreWorkers
  private val concurrency      = new AtomicInteger(0)

  private val idle = new MpmcUnboundedXaddArrayQueue[Worker](8)
  private val pool = Executors.newCachedThreadPool(Threads("kyo-worker", new Worker(_)))

  startWorkers()
  Coordinator.load()

  def removeWorker(): Unit =
    if (concurrencyLimit > coreWorkers)
      concurrencyLimit = Math.max(1, concurrency.get() - 1)

  def addWorker(): Unit = {
    concurrencyLimit = Math.max(concurrencyLimit, concurrency.get()) + 1
    startWorkers()
  }

  private def startWorkers(): Unit = {
    var c = concurrency.get()
    while (c < concurrencyLimit && concurrency.compareAndSet(c, c + 1)) {
      pool.execute(() => Worker().runWorker(null))
      c = concurrency.get()
    }
  }

  def flush(): Unit = {
    val w = Worker()
    if (w != null) {
      w.flush()
    }
  }

  def schedule(t: IOTask[_]): Unit = {
    val local = Worker()
    if (local != null && local.enqueueLocal(t)) {
      return
    }
    schedule(t, local)
  }

  @tailrec private[concurrent] def schedule(t: IOTask[_], submitter: Worker): Unit = {
    val w = idle.poll()
    if (w != null && w != submitter && w.enqueue(t)) {
      return
    }
    var w0: Worker = randomWorker(submitter)
    var w1: Worker = randomWorker(submitter)
    if (w0.load() > w1.load()) {
      val w = w0
      w0 = w1
      w1 = w
    }
    if (!w0.enqueue(t) && !w1.enqueue(t)) {
      schedule(t, submitter)
    }
  }

  def steal(thief: Worker): IOTask[_] = {
    // p2c load stealing
    var r: IOTask[_] = null
    var w0: Worker   = randomWorker(thief)
    var w1: Worker   = randomWorker(thief)
    if (w0.load() < w1.load()) {
      val w = w0
      w0 = w1
      w1 = w
    }
    r = w0.steal(thief)
    if (r == null) {
      r = w1.steal(thief)
    }
    r
  }

  def loadAvg(): Double = {
    var sum = 0L
    val it  = Worker.all.iterator()
    var c   = 0
    while (it.hasNext()) {
      sum += it.next().load()
      c += 1
    }
    sum.doubleValue() / c
  }

  def cycle(): Unit =
    Worker.all.forEach(_.cycle())

  def idle(w: Worker): Unit =
    if (w.load() == 0) {
      idle.add(w)
      w.park()
    }

  def stopWorker(): Boolean = {
    val c = concurrency.get()
    c > concurrencyLimit && concurrency.compareAndSet(c, c - 1)
  }

  private def randomWorker(besides: Worker): Worker = {
    var w: Worker = null
    while (w == null || w == besides) {
      try {
        val a = Worker.all
        w = a.get(XSRandom.nextInt(a.size()))
      } catch {
        case _: ArrayIndexOutOfBoundsException | _: IllegalArgumentException =>
      }
    }
    w
  }

  override def toString =
    s"Scheduler(loadAvg=${loadAvg()},concurrency=$concurrency,limit=$concurrencyLimit)"

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy