taso
10/17/2019, 6:48 PMKris Wong
10/17/2019, 6:51 PMkpgalligan
10/17/2019, 6:53 PMKris Wong
10/17/2019, 6:59 PMkpgalligan
10/17/2019, 7:11 PMKris Wong
10/17/2019, 7:55 PMkpgalligan
10/17/2019, 7:57 PMKris Wong
10/17/2019, 7:57 PMtaso
10/17/2019, 9:42 PMkpgalligan
10/17/2019, 10:00 PMexpect fun sendLog(l:LogData)
//Actually save to db
internal fun dbLog(l:LogData){
db.logQueries.insertLog(l)
}
//iOS
val worker = Worker.start()
actual fun sendLog(l:LogData){
worker.execute(TransferMode.SAFE, {l.freeze()}){
dbLog(it)
}
}
//Android
//Use coroutines, Executor service, whatever you'd normally do
actual fun sendLog(l:LogData){
//Android concurrency thing...
{
dbLog(it)
}
}
LogData
is whatever your log data is, obvmkojadinovic
10/18/2019, 6:51 AMinternal actual fun isPossibleToRunCoroutineInThisThread() = NSThread.isMainThread
private fun logAsync(
loggers: AtomicCollection<LoggerContract>,
log: (logger: LoggerContract) -> Unit
) {
if (isPossibleToRunCoroutineInThisThread()) {
launch {
loggers.forEach {
log(it)
}
}
} else {
loggers.forEach {
log(it)
}
}
}
I will definitely rethink it now. Especially, because I learned that NSThread.isMainThread
is not 100% reliable.kpgalligan
10/18/2019, 1:11 PMmkojadinovic
10/18/2019, 1:17 PMNSThread.isMainThread() is not reliable because in rare cases the main queue blocks, and GCD reuses the main thread to execute other queues.
kpgalligan
10/18/2019, 1:52 PMmkojadinovic
10/18/2019, 2:14 PMkpgalligan
10/18/2019, 2:16 PMLogger.kt
typealias AtomicCollection<T> = Collection<T>
interface LoggerContract{}
internal expect object Logger{
internal fun logAsync(
loggers: AtomicCollection<LoggerContract>,
log: (logger: LoggerContract) -> Unit
)
}
import kotlin.native.concurrent.TransferMode
import kotlin.native.concurrent.Worker
import kotlin.native.concurrent.freeze
internal actual object Logger {
private val worker = Worker.start()
internal actual fun logAsync(
loggers: AtomicCollection<LoggerContract>,
log: (logger: LoggerContract) -> Unit
) {
worker.execute(TransferMode.SAFE, {Pair(loggers, log).freeze()}){p ->
p.first.forEach {
p.second(it)
}
}
}
}
mkojadinovic
10/18/2019, 2:23 PMStately
helped me to understand how freeze woks for nativeactual class AtomicCollection<T> {
private val atomCollection = CopyOnWriteArrayList<T>()
actual fun clear() = atomCollection.clear()
actual fun add(element: T) = atomCollection.add(element)
actual fun forEach(action: (T) -> Unit) {
for (element in atomCollection) action(element)
}
}
actual class AtomicCollection<T> {
private val atomCollection = AtomicReference<List<T>>(ArrayList<T>().freeze())
@UseExperimental(InternalAPI::class)
private val lock = Lock()
actual fun clear() = modifyList { it.clear() }
actual fun add(element: T): Boolean = modifyList { it.add(element) }
actual fun forEach(action: (T) -> Unit) {
for (element in atomCollection.value) action(element)
}
@UseExperimental(InternalAPI::class)
private inline fun <R> modifyList(proc: (MutableList<T>) -> R): R {
lock.lock()
try {
val mutableList = ArrayList(atomCollection.value)
val result = proc(mutableList)
atomCollection.value = mutableList.freeze()
return result
} finally {
lock.unlock()
}
}
}
Lock
from ktor librarykpgalligan
10/18/2019, 2:28 PMArkadii Ivanov
10/18/2019, 9:03 PMTobi
10/27/2019, 2:26 PMcoroutineworker
library works around it:
https://www.droidcon.com/media-detail?video=362742333
tldr; on the JVM everything works as expected and on Kotlin/Native they launch a coroutine on a worker thread and poll from the caller thread until the work is done.mkojadinovic
10/28/2019, 1:43 PMkpgalligan
10/28/2019, 3:07 PMmkojadinovic
10/28/2019, 3:17 PM