Using 'withContext' to implement an Actor?


#1

I’ve got a class which is accessed via multiple contexts, and rather than having to lock the shared data I’d like to serialize all the processing onto a single thread. I don’t really like the overhead of having to map methods to messages like in an actor, though, so I tried doing something like this:

import kotlinx.coroutines.*
import java.lang.Thread.sleep
import kotlin.concurrent.thread

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")

val myContext = newFixedThreadPoolContext(1, "my context")

class Foo {
    suspend fun doWorkOne(p: Int) = withContext(myContext) {
        log("doWorkOne")
    }

    suspend fun doWorkTwo(e: Int) = withContext(myContext) {
        log("doWorkTwo")
    }
}

fun main(args: Array<String>) {
    val t = Foo()

    val t1 = thread {
        runBlocking {
            repeat (10) {
                log("Calling doWorkOne")
                t.doWorkOne(it)
                sleep(10)
            }
        }
    }

    val t2 = thread {
        runBlocking {
            repeat (10) {
                log("Calling doWorkTwo")
                t.doWorkTwo(it)
                sleep(10)
            }
        }
    }

    listOf(t1, t2).forEach { it.join() }
}

Does this seem like a reasonable approach? Is there a better way to achieve this?


#2

Actually I think this is slightly better, as I don’t need to wait for the calls to finish so the caller doesn’t need to be within a coroutine:

object MyScope : CoroutineScope {
    override val coroutineContext: CoroutineContext = newFixedThreadPoolContext(1, "my context")
}

class Foo {
    fun doWorkOne(p: Int) = MyScope.launch {
        log("doWorkOne $p")
    }

    fun doWorkTwo(e: Int) = MyScope.launch {
        log("doWorkTwo $e")
    }
}

fun main(args: Array<String>) {
    val t = Foo()

    val t1 = thread {
        repeat (10) {
            log("Calling doWorkOne $it")
            t.doWorkOne(it)
            log("doWorkOne $it done")
        }
    }

    val t2 = thread {
        repeat (10) {
            log("Calling doWorkTwo $it")
            t.doWorkTwo(it)
        }
    }

    sleep(100)
    listOf(t1, t2).forEach { it.join() }
}