Is it possible to create a dispatcher that only al...
# coroutines
b
Is it possible to create a dispatcher that only allows running on the same thread as I'm currently running on? I thought this could do the trick:
Copy code
Executors.newSingleThreadExecutor { Thread.currentThread() }.asCoroutineDispatcher()
But I'm getting
java.lang.IllegalThreadStateException
when using that dispatcher.
s
That code won't do what you want it to. Maybe you're just looking for
runBlocking
?
b
No I don't want to block, and I'm already in a suspend function. But I want to be able to "lock in" on the thread I've been given and not switch, Unfortunately, It's not a single-thread dispatcher that called me - in that case I could've just kept running. I want the behaviour of say
Dispatchers.Main
but using the thread I'm currently on.
s
If you don't want to block, then by definition you want to suspend, which by definition releases the current thread back to the dispatcher it came from. At that point some other code, in the dispatcher implementation or elsewhere, becomes responsible for choosing what runs on that thread next. So the solution you're describing isn't possible in the general case. What problem are you trying to solve? Perhaps there's a different approach.
b
Well, I need to make sure that certain operations execute on the same thread, transactions for example, but I still want to use suspension and structured concurrency. And while suspending it doesn't matter if someone else uses the thread. But since coming in from a dispatcher that allows multiple threads to pickup, it's not suitable for my case.
s
The solution might depend on what libraries and frameworks you're using. If you have access to non-blocking/reactive transactions you can much more easily adapt those to coroutines. In Spring it's even supported out of the box.
b
I'm using micronaut, jpa, hibernate reactive and vert.x. that's when the issue occured. That we got an inner exception that hibernate throws an error if used from different threads. But yeah, might need to restructure a bit since there seem to not be fully compatible with each other.
s
Just calling another suspend function will call that suspend function likely on the same thread (thread affinity), but it's not guaranteed. I'd create a brand new single threaded dispatcher, use that dispatcher in a class that gives access to, (wraps) that "single thread is required" component and do a withContext(mySingleThreadedDispatcher) every time I call a method on that component.
👍 1
a
It is possible to do this yes, but it is odd. Here is a code snippet for you
Copy code
class SynchronousExecutor : AbstractExecutorService(), ExecutorService {
    var active = true

    override fun execute(command: Runnable) {
        command.run()
    }

    override fun shutdown() {
        active = false
    }

    override fun shutdownNow(): MutableList<Runnable> {
        active = false
        return mutableListOf()
    }

    override fun isShutdown(): Boolean = !active

    override fun isTerminated(): Boolean = !active

    override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
        return true
    }
}

fun synchronousDispatcher() = SynchronousExecutor().asCoroutineDispatcher()

suspend fun test() {
   withContenext(synchronousDispatcher()) {
      // do your thing here
   }
}
s
I think that would behave more like Dispatchers.Unconfined, if I'm reading it correctly
a
The difference being (as per my understanding), resumes will continue in the same thread. Unconfined dispatcher would start in the same thread (like this one), but can resume in any available thread (sometimes even the current one)
s
I don't think the code you shared will be able to achieve what you're hoping, since
execute
will not be called from the original thread after a resume
a
To my knowledge,
execute
is the method responsible for switching threads. Not
execute
's caller. Am I wrong?
s
That sounds right, but
execute
will simply be called from whichever thread calls
resume
-- not from the thread that created the executor
a
and in what conditions would
resume
be called from a separate thread??
s
A suspension point is backed by an asynchronous callback, and the callback is what ultimately calls
resume
. So the thread varies depending on what library provides the underlying callback.
Normally the dispatcher would then switch it to one of its own threads before actually invoking the continuation. When we say
Dispatchers.Unconfined
can resume on an arbitrary thread, what that really means is it will just resume directly on whatever thread triggers the coroutine to resume.
a
Actually that is correct. Especially if someone switches context within, a couroutine backed by this dispatcher, then yes. But if there is no coroutineContext switching, I don't think there will be any thread switching. No?
s
Every suspension point vacates its thread when it suspends, and dispatches again when it resumes. So any suspension point has the potential to result in a thread switch, depending on the dispatcher implementation.