Piasy
12/01/2019, 3:55 AMCounter
, provides some operations, e.g. increase
and decrease
, these operations could be called from main thread or other worker threads, but Counter
will schedule these calls into its own single threaded message queue, then change its state, e.g. the count
, it may also output its state through some callback, and the callback should be invoked on main thread.
On Android, we could using Handler or Executor to implement this pattern easily, I’m thinking if it’s possible to do it on Kotlin/Native. I want to develop a Kotlin multiplatform app and want to use this pattern in it.
I tried worker
, but everything submitted to worker must be frozen, so I can’t update Counter
’s state.
I also tried coroutine
, but on Kotlin/Native, coroutine only works for main thread, although there is a preview version supporting multi-thread coroutine, but when I try to launch a coroutine on the dispatcher created by newSingleThreadContext("test")
, it fails with “This dispatcher can be used only from a single thread test, but now in MainThread”.olonho
12/01/2019, 8:29 AMPiasy
12/01/2019, 8:31 AMWorker
, the captured objects are frozen, then I can't change it's state inside the lambda posted to Worker
.Counter
from multiple threads, then I don't need to consider concurrency issue any more.olonho
12/01/2019, 8:33 AMAtomicInt
, even being frozen increment
operation is still available.Piasy
12/01/2019, 8:34 AMcount
now) of Counter
into a immutable data class called CounterState
, and define a class TaskQueue
which holds a CounterState
:
expect class TaskQueue<T>(state: T) {
fun post(task: () -> Unit)
fun getState(): T
fun setState(newState: T)
}
Android actual class is:
actual class TaskQueue<T> actual constructor(private var state: T) {
private val executor = Executors.newSingleThreadExecutor()
actual fun post(task: () -> Unit) {
executor.execute(task)
}
actual fun getState(): T = state
actual fun setState(newState: T) {
state = newState
}
}
iOS actual class is:
actual class TaskQueue<T> actual constructor(state: T) {
private val worker = Worker.start()
private val state = AtomicReference(state.freeze())
actual fun post(task: () -> Unit) {
worker.executeAfter(0, task.freeze())
}
actual fun getState(): T = state.value
actual fun setState(newState: T) {
newState.freeze()
while (!state.compareAndSet(state.value, newState)) {
}
}
}
and the increment
fun of Counter
is (in common module):
fun increment() {
<http://taskQueue.post|taskQueue.post> {
taskQueue.setState(CounterState(taskQueue.getState().count + 1))
}
}
It works as expected. How does it look like to you?