Scheduler

Summary:
Add a simple scheduler which operates in the following way.

There are two type of tasks: main, background.

The main task will run on the main thread whilst the background task will run on a background thread.

The main task will be executing at a fixed internal whereas the background task will get queued on demand but can effectively consume what was produced by the main task at its own rate.

Reviewed By: LukeDefeo

Differential Revision: D38975283

fbshipit-source-id: 0633385d2938705a16f5fc75a28cad067e4a8e55
This commit is contained in:
Lorenzo Blasa
2022-08-24 09:03:39 -07:00
committed by Facebook GitHub Bot
parent 2e33febcde
commit 1b2f875cc6

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.flipper.plugins.uidebugger.scheduler
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
class Scheduler<T>(val task: Task<T>, val rate: Long = 500L) {
interface Task<T> {
fun execute(): T?
fun process(input: T)
}
private val mainLooper: Handler = Handler(Looper.getMainLooper())
private val mainRunnable = MainThreadRunnable()
private val backgroundRunnable = BackgroundThreadRunnable()
private var backgroundHandler: HandlerThread? = null
private var backgroundLooper: Handler? = null
private var isRunning = false
private val lock = ReentrantLock()
private val condition = lock.newCondition()
private val queue = mutableListOf<T>()
fun start() {
backgroundHandler = HandlerThread("INSPECTOR_WORKER")
backgroundHandler?.let { handlerThread ->
handlerThread.start()
backgroundLooper = Handler(handlerThread.looper)
}
isRunning = true
mainLooper.postDelayed(mainRunnable, rate)
}
fun stop() {
backgroundLooper?.post(CancellationRunnable())
}
fun execute() {
if (Looper.myLooper() == Looper.getMainLooper()) {
mainRunnable.run()
} else {
mainLooper.post(mainRunnable)
}
}
inner class MainThreadRunnable : Runnable {
override fun run() {
if (!isRunning) {
return
}
try {
val output = task.execute()
output?.let { output ->
lock.withLock {
queue.add(output)
condition.signal()
}
}
} catch (e: Exception) {}
mainLooper.postDelayed(mainRunnable, rate)
backgroundLooper?.post(backgroundRunnable)
}
}
inner class BackgroundThreadRunnable : Runnable {
override fun run() {
if (!isRunning) {
return
}
try {
var input: T?
lock.withLock {
while (queue.isEmpty()) {
condition.await()
}
input = queue.removeFirst()
}
input?.let { input -> task.process(input) }
} catch (e: Exception) {}
}
}
inner class CancellationRunnable : Runnable {
override fun run() {
backgroundHandler?.interrupt()
mainLooper.removeCallbacks(mainRunnable)
backgroundLooper?.removeCallbacks(backgroundRunnable)
backgroundHandler = null
isRunning = false
}
}
}