incorporate androidlib

This commit is contained in:
andrekir 2022-09-04 22:52:40 -03:00
parent 20cf3f0825
commit 5eb5cd1421
63 changed files with 1451 additions and 108 deletions

View file

@ -0,0 +1,29 @@
package com.geeksville.mesh.concurrent
import com.geeksville.mesh.util.Exceptions
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
private val errorHandler =
CoroutineExceptionHandler { _, exception ->
Exceptions.report(
exception,
"MeshService-coroutine",
"coroutine-exception"
)
}
/// Wrap launch with an exception handler, FIXME, move into a utility lib
fun CoroutineScope.handledLaunch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
) = this.launch(
context = context + com.geeksville.mesh.concurrent.errorHandler,
start = start,
block = block
)

View file

@ -0,0 +1,30 @@
package com.geeksville.mesh.concurrent
import com.geeksville.mesh.android.Logging
/**
* Sometimes when starting services we face situations where messages come in that require computation
* but we can't do that computation yet because we are still waiting for some long running init to
* complete.
*
* This class lets you queue up closures to run at a later date and later on you can call run() to
* run all the previously queued work.
*/
class DeferredExecution : Logging {
private val queue = mutableListOf<() -> Unit>()
/// Queue some new work
fun add(fn: () -> Unit) {
queue.add(fn)
}
/// run all work in the queue and clear it to be ready to accept new work
fun run() {
debug("Running deferred execution numjobs=${queue.size}")
queue.forEach {
it()
}
queue.clear()
}
}

View file

@ -0,0 +1,82 @@
package com.geeksville.mesh.concurrent
import com.geeksville.mesh.android.Logging
/**
* A deferred execution object (with various possible implementations)
*/
interface Continuation<in T> : Logging {
abstract fun resume(res: Result<T>)
// syntactic sugar
fun resumeSuccess(res: T) = resume(Result.success(res))
fun resumeWithException(ex: Throwable) = try {
resume(Result.failure(ex))
} catch (ex: Throwable) {
// errormsg("Ignoring $ex while resuming, because we are the ones who threw it")
throw ex
}
}
/**
* An async continuation that just calls a callback when the result is available
*/
class CallbackContinuation<in T>(private val cb: (Result<T>) -> Unit) : Continuation<T> {
override fun resume(res: Result<T>) = cb(res)
}
/**
* This is a blocking/threaded version of coroutine Continuation
*
* A little bit ugly, but the coroutine version has a nasty internal bug that showed up
* in my SyncBluetoothDevice so I needed a quick workaround.
*/
class SyncContinuation<T> : Continuation<T> {
private val mbox = java.lang.Object()
private var result: Result<T>? = null
override fun resume(res: Result<T>) {
synchronized(mbox) {
result = res
mbox.notify()
}
}
// Wait for the result (or throw an exception)
fun await(timeoutMsecs: Long = 0): T {
synchronized(mbox) {
val startT = System.currentTimeMillis()
while (result == null) {
mbox.wait(timeoutMsecs)
if (timeoutMsecs > 0 && ((System.currentTimeMillis() - startT) >= timeoutMsecs))
throw Exception("SyncContinuation timeout")
}
val r = result
if (r != null)
return r.getOrThrow()
else
throw Exception("This shouldn't happen")
}
}
}
/**
* Calls an init function which is responsible for saving our continuation so that some
* other thread can call resume or resume with exception.
*
* Essentially this is a blocking version of the (buggy) coroutine suspendCoroutine
*/
fun <T> suspend(timeoutMsecs: Long = -1, initfn: (SyncContinuation<T>) -> Unit): T {
val cont = SyncContinuation<T>()
// First call the init funct
initfn(cont)
// Now wait for the continuation to finish
return cont.await(timeoutMsecs)
}