Coroutine context to protect resource


#1

Often I want to protect access to a resource by forcing coroutines to be executed in serial fashion like a FIFO queue. Kotlin allows me to do this with newSingleThreadContext(), for example:

class ProtectedResource {
  private val ctx = newSingleThreadContext("MyResource")

  suspend fun connect() = run(ctx) {
  }

  suspend fun disconnect() = run(ctx) {
  }
}

This works well if for example someone calls disconnect() directly after connect().

But am I actually reserving a whole thread here? What I really want to say is: Make sure these methods always run serially. Or: Make sure they are always scheduled on the same thread. I don’t care which thread. Often it could really be one of CommonPool. But it should stay the same thread in the end. Is there a way to do that? Should there be?

(For comparison, there is something called serial dispatch queues in Apple’s Foundation framework that allow me to do something this. I would schedule tasks on such a queue and this queue executes tasks on the same thread. I can have multiple such queues that in the end schedule on the same thread, though.)


#2

For your explanation I assume that you need to make sure that connect and disconnect are not executed concurrently, that is your requirement is that the implementations of connect and disconnect functions are invoked serially. You’ve correctly identified one way to achieve this – using confinement to a single-threaded context. There are other ways, namely using Mutex and using an actor. They are all contrasted and explained in the “Shared Mutable State and Concurrency” section of the guide: https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#shared-mutable-state-and-concurrency


#3

Yes, I want the functions to be invoked serially. My question was: Can I do this without reserving a thread? Is that what happens when I use a single-threaded context?

Actors would be a great solution but then I have to translate my method calls into serialized messages. Compared to the example above I’d end up writing quite a bit more.


#4

Yes. You can do it. Reserving a thread is just one possible solution. I really suggest to read the document I’ve linked to. You’ve missed the solution with Mutex that also solves the same problem without reserving a thread and without converting method calls to messages.


#5

Sorry, if I sound too much like I didn’t read the document. I did read it several times and probably my brain unfairly rejects any solution with a mutex automatically :slight_smile: Thanks for your time. And for the coroutines.


#6

There is nothing inherently wrong with Mutex, because it specifically and explicitly designed to serialize execution of code block just when you need it.