Theoretical question, if one was to use kotlin nat...
# kotlin-native
s
Theoretical question, if one was to use kotlin native to create something like a connection pool is that possible? My thought immediately was to see what I could do with workers. However any time I start to wrap the
Worker.execute
method at all, I'm confronted with:
Copy code
kotlin.native.concurrent.Worker.execute must take an unbound, non-capturing function or lambda
Essentially lambda could be passed as data to worker or async queue
s
I was hoping to do something like this:
Copy code
class WorkerManager() {
    internal val readwriteWorker =  Worker.start()
    internal var readwriteDB = Database.open(path)

    fun readWriteAsync(job: (db) -> String) {
        readwriteWorker.execute(TransferMode.SAFE, { readwriteDB }, job)
    }
}
If I understand correctly, I would need to call the producer function and detach it's results as well has the job function itself and then reattach them inside of my own worker job block?
Something like this:
Copy code
internal val readwriteWorker =  Worker.start()
    internal var readwriteDB = Database.open(path)

    fun <T1, T2>readWriteAsync(producer: ()->T1, job: (T1) -> T2): Future<T2> {
        val inputPtr = DetachedObjectGraph {
            Pair(producer(), job)
        }.asCPointer()

        return readwriteWorker.execute(TransferMode.SAFE, { inputPtr }) { ptr ->
            val result = DetachedObjectGraph<Pair<T1, (T1) -> T2>>(ptr).attach()
            result.second(result.first)
        }
    }
o
for workers such complexity is not needed, smth like
Copy code
data class Input(var int: Int)
data class Output(var string: String)

inline fun <reified T, reified R> Worker.executeAsync(crossinline producerConsumer: () -> Pair<T, (T) -> R>): Future<R> =
    execute(TransferMode.SAFE, { producerConsumer() }) { it -> it.second(it.first) }

fun main() {
    val worker = Worker.start()
    val future = worker.executeAsync { Pair<Input, (Input) -> Output>(Input(42), { input ->
        <http://input.int|input.int>++
        Output(input.int.toString())
    })}
    println(future.result)
}
shall do
s
After much trial and error, I think I have something workable. My plan is to have all writes happen to one worker while reads will get distributed over a pool of workers with read only access to the database file. https://github.com/samus/slkndb/blob/master/src/main/kotlin/com.synappticlabs.db.sqlite3/DatabaseWorkerManager.kt
o
not sure if it’s exactly what you need, as
freeze
will affect the job you passed. I’d suggest smth similar to approach above.
s
Can I do something like that and still be able to pass in extra context data that the caller won’t have access to?
o
not in the strict mode, where runtime ensures that there’s no concurrency problems, but we’re considering implementing relaxed mode, where this will be possible at cost of potential runtime bugs. You can achieve similar effect although with trick like that.
Copy code
data class Input(var int: Int)

class Box<T>(boxed: T) {
    private var value_: T? = boxed
    val value: T get() {
        val result = value_!!
        value_ = null
        return result
    }
}

inline fun <reified T, reified R> Worker.executeAsync(data: Box<T>, crossinline consumerMaker: () -> ((T) -> R)): Future<R> =
        execute(TransferMode.SAFE, {
        Pair(data.value, consumerMaker())
    }) { it -> it.second(it.first) }

fun main() {
    val worker = Worker.start()
    val boxedInput = { Box(Input(42)) } ()
    val future = worker.executeAsync(boxedInput) { { input: Input ->
        <http://input.int|input.int>++
        input
    } }
    println(future.result)
}