diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 8a98267..4e9cc28 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -503,7 +503,7 @@ style: MagicNumber: active: true excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - ignoreNumbers: '-1,0,1,2,100,500,1000' + ignoreNumbers: '-1,0,1,2,3,42,100,500,1000,4242' ignoreHashCodeFunction: true ignorePropertyDeclaration: false ignoreLocalVariableDeclaration: false diff --git a/src/main/kotlin/util/future/Future.kt b/src/main/kotlin/util/future/Future.kt new file mode 100644 index 0000000..d09b1ce --- /dev/null +++ b/src/main/kotlin/util/future/Future.kt @@ -0,0 +1,66 @@ +package util.future + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +interface Future { + suspend fun get(): T + suspend fun get(timeout: Long): T? + fun isDone(): Boolean + fun cancel() + fun isCancelled(): Boolean +} + +class CoFuture(private val func: suspend () -> T) : Future { + private var value: T? = null + private var job = GlobalScope.launch { + value = func() + } + + override suspend fun get(): T { + job.join() + return value!! + } + + override suspend fun get(timeout: Long): T? { + delay(timeout) + return value + } + + override fun isDone(): Boolean = job.isCompleted + + override fun cancel() { + job.cancel() + } + + override fun isCancelled(): Boolean = job.isCancelled +} + +fun future(func: suspend () -> T): Future = CoFuture(func) + +fun delayedEcho(value: T, delay: Long = 1000): Future = future { + delay(delay) + value +} + +class MathFuture { + fun add(a: Int, b: Int): Future = future { a + b } +} + +fun main() = runBlocking { + val future = delayedEcho(42, delay = 2000) + println("do other work.") + println(future.get()) + + val math = MathFuture() + val sums = (0 until 100) + .map { math.add(it, it * 2) } + + println("End all sums.") + + sums.forEach { + println(it.get()) + } +} diff --git a/src/main/kotlin/util/future/FutureHandler.kt b/src/main/kotlin/util/future/FutureHandler.kt new file mode 100644 index 0000000..9c1d823 --- /dev/null +++ b/src/main/kotlin/util/future/FutureHandler.kt @@ -0,0 +1,45 @@ +package util.future + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method +import java.lang.reflect.Proxy +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit + +class FutureHandler(private val target: Any) : InvocationHandler { + private val queue = LinkedBlockingQueue>() + + override fun invoke(proxy: Any?, method: Method?, args: Array?): Any { + val future = method!!.invoke(target, *args!!) as LatchFuture<*> + queue.add(future) + + return future + } + + fun start(): Job { + return GlobalScope.launch { + while (isActive || queue.isNotEmpty()) { + queue.poll(100, TimeUnit.MILLISECONDS) + ?.exec() + } + } + } +} + +fun Any.toFutureHandler(inter: Class): Pair { + val futureHandler = FutureHandler(this) + + val job = futureHandler.start() + + return Pair( + Proxy.newProxyInstance( + this::class.java.classLoader, + arrayOf(inter), + futureHandler + ) as T, job + ) +} diff --git a/src/main/kotlin/util/future/LatchFuture.kt b/src/main/kotlin/util/future/LatchFuture.kt new file mode 100644 index 0000000..be466f1 --- /dev/null +++ b/src/main/kotlin/util/future/LatchFuture.kt @@ -0,0 +1,38 @@ +package util.future + +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit + +class LatchFuture(private val func: () -> T) : Future { + private val latch = CountDownLatch(1) + private var isCancelled = false + private var value: T? = null + + override fun isDone(): Boolean = value != null + + override fun get(): T { + latch.await() + return value!! + } + + override fun get(p0: Long, p1: TimeUnit): T? { + p1.sleep(p0) + return value + } + + override fun cancel(p0: Boolean): Boolean { + isCancelled = true + latch.countDown() + return isCancelled + } + + override fun isCancelled(): Boolean = isCancelled + + fun exec() { + if (!isCancelled) { + value = func() + latch.countDown() + } + } +} diff --git a/src/main/kotlin/util/future/Main.kt b/src/main/kotlin/util/future/Main.kt new file mode 100644 index 0000000..c6d5c2e --- /dev/null +++ b/src/main/kotlin/util/future/Main.kt @@ -0,0 +1,24 @@ +package util.future + +import kotlinx.coroutines.cancelAndJoin +import kotlinx.coroutines.runBlocking +import java.util.concurrent.Future +import kotlin.time.ExperimentalTime +import kotlin.time.measureTime + +@ExperimentalTime +fun main() = runBlocking { + val (math, job) = FutureMathImpl().toFutureHandler(FutureMath::class.java) + + lateinit var sums: List> + + val elapsed = measureTime { + sums = (0 until 100).map { math.add(it, it * 2) } + } + + println("Elapsed time: $elapsed") + + sums.forEach { println(it.get()) } + + job.cancelAndJoin() +} diff --git a/src/main/kotlin/util/future/Math.kt b/src/main/kotlin/util/future/Math.kt new file mode 100644 index 0000000..c1b6592 --- /dev/null +++ b/src/main/kotlin/util/future/Math.kt @@ -0,0 +1,13 @@ +package util.future + +import java.util.concurrent.Future + +interface FutureMath { + fun add(a: Int, b: Int): Future +} + +class FutureMathImpl : FutureMath { + override fun add(a: Int, b: Int): Future { + return LatchFuture { a + b } + } +}