Phil Richardson
11/15/2021, 12:36 PM<http://Dispatchers.IO|Dispatchers.IO>
, it does not fit into the use case in the slightest, as you still have a thread pool backing it
This is also a point at which coroutines start to confuse me some more.
Even if this IO dispatcher was single threaded, any suspending operations that happen can do just that, which could result in improper interleaving of two coroutine instances wanting to create an entry in this TarArchiveOutputStream
On the face, it would imply I should use runBlocking to ensure suspending operations don't suspect but get run to completion
But that still leaves the fact there can be more than one thread in the IO dispatcher don't this at the same time.
That then also implies I could create my own dedicated single thread executor service as a dispatcher
Then with any coroutine launched inside of it, use runBlocking here to complete the create entry, write stream close entry elements
But then this still tells me I am running afoul of the runBlocking rule that I should not use runBlocking from within a coroutine
So overall, are there any good examples of using something like the IO dispatcher for i/o on something that is not thread safe?Joffrey
11/15/2021, 12:48 PMTarArchiveOutputStream
is not thread safe isn't related to the fact that it's based on blocking IO.
any suspending operations that happen can do just thatDo what? Suspend? Yes, but if you're dealing with a blocking API, there is likely no suspend operation here. But in any case, I think what you're looking for is a coroutine Mutex. You can just do your operations under a mutex associated with your output stream (from any coroutine), and you'll be fine. You can also nest this under a
withContext(<http://Dispatchers.IO|Dispatchers.IO>)
to solve the blocking-IO-in-coroutine problem (but these are really 2 different problems)Joffrey
11/15/2021, 12:54 PMval tarArchiveStream = TODO("whatever constructor or method gives you the tar archive stream")
val tarStreamMutex = Mutex()
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
tarStreamMutex.withLock {
// create an entry, add it to the stream, close the entry
}
}
Joffrey
11/15/2021, 12:57 PMTarArchiveOutputStream
that contains the mutex inside and provides atomic operations as methodsJoffrey
11/15/2021, 1:16 PMThat then also implies I could create my own dedicated single thread executor service as a dispatcher. Then with any coroutine launched inside of it, use runBlocking here to complete the create entry, write stream close entry elements. But then this still tells me I am running afoul of the runBlocking rule that I should not use runBlocking from within a coroutineIndeed, you should not use
runBlocking
inside a coroutine. It's true that on a single-threaded dispatcher, it would kind of have the effect of locking, but it's not supposed to be used as a synchronization primitive. Mutex is meant for synchronization, so you should rather use this.ephemient
11/15/2021, 6:35 PMephemient
11/15/2021, 6:37 PMJoffrey
11/15/2021, 6:38 PMuli
11/15/2021, 8:36 PMJoffrey
11/15/2021, 8:37 PMephemient
11/15/2021, 8:39 PMTarArchiveOutputStream
case, but it might for other APIs use their own recursive locks or thread local variables, for example), then you either need to switch to a single-threaded dispatcher or ensure that there are no suspend points within your "critical section" (can be guaranteed by writing it in a non-suspend fun, for example)