DEV Community

Discussion on: Why I stopped using Coroutines in Kotlin

 
fkirc profile image
Felix K • Edited

kotlinx.coroutines.sync.mutex is a mutex implementation that works with coroutines. If guava was using this mutex implementation, then it might work. Alternatively, guava could release all its internal locks when executing your loading function. If your loading function needs special synchronization, then you can still implement that without relying on the guava locking. Besides, recursively loading the same key seems like a strange edge case. One should not query guava from within the loading function that is supposed to retrieve the very same key. The protection from this error is nice to have but not a major dealbreaker.

Thread Thread
 
martinhaeusler profile image
Martin Häusler • Edited

Nevertheless, recursively loading the same key seems like a strange edge case. One should not query guava from within the loading function that is supposed to retrieve the very same key.

Indeed it is, and not what the developer wants. Which is why guava raises an exception if this case occurs. But take a step back and think about how guava accomplishes that. It checks internally if the current thread holds the loading lock, and if so, it checks the key it wants to load. If it is the same as during the previous call, the exception is thrown. Now, let's assume the loader is async. What would that mean? Well, the coroutine may yield during loading (e.g. when performing an HTTP request to fetch the data) and the host thread will merrily move along and pick up the next coroutine. It still holds the lock, however. This has two fatal consequences:

  • when our loader finishes, there is no guarantee that it will be exe cuted by the same thread which holds the appropriate lock.
  • the thread which holds the lock may host a coroutine which may query the same missing key. Guava will throw an exception here, thinking that it is dealing with recursive load calls to the same key, when in reality that is not the case.

Using coroutines in an environment which isn't specifically crafted for them (read: 99.9% of all JVM libraries in existence) means opening pandoras box. This is precisely what the fancy presentations will not tell you. And the reason why I refactored a lot of code to eliminate coroutines entirely. I've never looked back.

Thread Thread
 
fkirc profile image
Felix K • Edited

Either you revert it, or you adapt your code to cope with new paradigms. As outlined, this should be easy to fix for the guava developers. In the meantime, a possible workaround is to wrap your loading functions inside a runBlocking scope. According to the docs:

runBlocking is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main functions and in tests.

In fact, I believe that Kotlin enables a more gradual transition to coroutines than building coroutines into the JVM. Adding a few Kotlin coroutines might be easier than switching a myriad of libraries to a new JVM version.