From 1b2f875cc68cf005a014fb6487697091f6c1f52f Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 24 Aug 2022 09:03:39 -0700 Subject: [PATCH] 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 --- .../plugins/uidebugger/scheduler/Scheduler.kt | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 android/src/main/java/com/facebook/flipper/plugins/uidebugger/scheduler/Scheduler.kt diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/scheduler/Scheduler.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/scheduler/Scheduler.kt new file mode 100644 index 000000000..030cc9055 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/scheduler/Scheduler.kt @@ -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(val task: Task, val rate: Long = 500L) { + interface Task { + 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() + + 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 + } + } +}