I'm trying to write an API which will be called fr...
# coroutines
a
I'm trying to write an API which will be called from Java so I'm using this construct:
Copy code
runBlocking {
    withContext(someContext) {
        // ...
    }
}
My problem is that these functions will be called from each other which would lead to nested `runBlocking`/`withContext` calls. I tried how this works here:
Copy code
runBlocking {
    println("In runBlocking 0")
    println("Current thread: ${Thread.currentThread().name}")
    withContext(Dispatchers.Single) {
        println("In withContext 0")
        println("Current thread: ${Thread.currentThread().name}")
        runBlocking {
            println("In runBlocking 1")
            println("Current thread: ${Thread.currentThread().name}")
            withContext(Dispatchers.Single) {
                println("In withContext 1")
                println("Current thread: ${Thread.currentThread().name}")
            }
        }
    }
}
but the problem is that here
In withContext 1
never gets printed. What is the best practice when one needs to nest these calls but has to keep the API accessible from Java?
l
Why do you use
withContext
there? And why do you nest
runBlocking
?
a
i need to use
withContext
because I use a single threaded dispatcher to make sure that all calls are executed in sequence
i'm modifying shared mutable state
runBlocking
is necessary because this will be called from Java
i could use actors instead but they are deprecated in Kotlin
d
You can also do
runBlocking(someContext)
to elide the
withContext
.
In withContext 1
never gets printed because of deadlock.
The reason is similar to why there's no
runBlocking
in javascript but I can't articulate it.
a
why is there a deadlock?
d
Basically the second
withContext
is waiting for the first
withContext
to finish.
a
oh wait
it tries to block until the second one finishes, but it can't since its parent is also blocking and the dispatcher executes serially
right?
d
Yeah
a
i see the problem
i think i'll just implement
CoroutineScope
and use
launch
instead
d
You can always return
CompletableFuture<...>
to java (eliminating the need for
runBlocking
).
a
i can call
launch
within another
launch
i'm in an MPP environment
so my
runBlocking
is just an
actual
function
I use
TODO()
in javascript 😄
d
Ha
a
what returns
CompletableFuture
?
how would that look like?
i'm trying to avoid creating problems when I call a function which launches a coroutine from another function which launches its own
but in theory coroutines get nested so it is not a problem
d
You can make an `expect`/`actual`
typealias
for
CompletableFutute<...>
,
Promise<....>
and
Deferred<....>
. Although I haven't tried this myself.
i'm trying to avoid creating problems when I call a function which launches a coroutine from another function which launches its own
Can you re-articulate this?
a
this would work, right?
Copy code
class Foo(
        override val coroutineContext: CoroutineContext = Dispatchers.Single)
    : CoroutineScope {

    fun fun0() {
        launch { 
            fun1()
        }
    }
    
    fun fun1() {
        launch { 
            // ...
        }
    }
}
the main point is that my users will call both
fun0
and
fun1
d
Oh, is that what you mean.
a
but both need to work with coroutines
d
I think, you might be better of with a wrapper class.
a
how would that work?
d
Where the underlying
Foo
is suspend but the exposed
Foo
just calls
runBlocking
on the underlying
Foo
,.
a
that's the problem
oh wait
the problem with that is that I don't want to have duplicated interfaces
so what i'm writing has a synchronous api
but it works with shared mutable state
d
Oh, you just want to use coroutines to help with sync.
a
yes
although i've written a test
and to my biggest surprise
using
Mutex
was faster than thread confinement
and using the deprecated
actor
function was faster than a
Mutex
d
I think
actor
is the way to go.
a
my problem with actors though is that it is deprecated and it needs a lot of boilerplate
i've watched several videos about coroutines in Kotlin
but I still don't know what happens if I do this:
Copy code
withContext(coroutineContext) {
    withContext(coroutineContext) {

    }
}
d
That should no-op.
a
will there be a context switch?
or will it recognize that i'm in the same context?
i tried to decompile the java bytecode
d
withContext
does a check for similar contexts.
a
but i ended up with a 28MB file
oh i see
so i should just drop the whole thing and use the deprecated
actor
function anyway?
and write the boilerplate?
d
a
checking
thanks 👍
hm
i might be better off using a
Mutex
it is only marginally slower and I don't have much writes
d
Yeah, that works too. Just remember to unlock it before calling another method.
a
i'd have a mutex in each object
which restrict writes to that specific object
d
Yes, but if a method holding a lock calls another method that wants to acquire the lock then 💥 .
a
yep, but I don't do that
i'm only locking in like 2-3 methods which implement the most basic operations and everything else is built on top of them
d
Oh okay,
a
i made this possible by using persistent data structures internally so all reads are consistent
and data is easy to transform
you mentioned
CompletableFuture
before
what returns this?
async
?
d
future
GlobalScope.future { ... }
a
oh
i don't see this method
is this available in an MPP environment?
i only see
async
d
It's available in
kotlinx-coroutines-jdk8
I think.
a
although i don't really need this because all my mutating functions are returning
Unit
i'm working on an MPP project
d
Yeeah
You don't need this
a
👍
the only thing i need is to block until the coroutine completes
that's what I don't know how to do without
runBlocking
i can just call
launch
in all my functions and be done with it but in that case the control is given back to the user without the operation finished
d
There's no other way. Blocking is blocking. You still need to block?
a
the API is synchronous
d
Or rather. Blocking is still an issue?
a
i just want some kind of way to make it thread-safe
another option is to have an `expect`/`actual` construct for
Deferred
which the user can call
await
on
the problem with
Deferred
is that
await
is
suspend
so it can't be used from Java
😒
d
Then I guess you stuck with the wrapper class idea.
Or some variant of it.
a
that won't work
i'd need to duplicate a lot of the interfaces
why there is no built-in way in the coroutines api to help with java interop?
d
Not the interfaces, just make an inner class.
Nope! Just
CompletableFuture
.
a
what do you mean by the inner class?
it might be easier to make an `expect`/`actual` class which provides a blocking
await
function
for Java users
d
Copy code
class Foo : Interface1, Interface2 {
   fun stuff() = runBlocking { inten.stuff() }

   private class FooInternal {
        suspend fun stuff()
   }
}
Or maybe use a real
Mutex
.
a
like this?
Copy code
runBlocking {
    mutex.withLock {
        state = state.put(key, value)
    }
}
d
Isn't that what you were already doing?
a
yeah, that's the problem 😄
plus this wont' work in my js implementation
man this is hard
d
Or maybe use a real
Mutex
I meant a blocking one.
a
like
@Synchronized
?
d
Yeah!
a
hm
and this would automatically work in js as well
since
@Synchronized
would be just ignored
d
Just no-op in JS, since no concurrency anyway.
a
yep
why didn't I think about this earlier???
😂
d
Aha, lol.
a
man you're a genius
thanks!
d
No problem!
a
👍
Copy code
/**
 * Marks the JVM method generated from the annotated function as `synchronized`, meaning that the method
 * will be protected from concurrent execution by multiple threads by the monitor of the instance (or,
 * for static methods, the class) on which the method is defined.
 */
@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@MustBeDocumented
@OptionalExpectation
public expect annotation class Synchronized()
so what this means is that if I have multiple
@Synchronized
functions in a class only one of them will have access to the monitor, eg: only one of them will be running at any given time
right?
d
Yeah
a
this is exactly what i need
man this is awesome
i can delete so much code now
i just ran it through my benchmark and it is 8x times faster than a Kotlin Mutex
d
Nice!
a
you can't imagine how happy i am now
l
Beware you can still have mutable shared state issues with a single thread as parallel coroutines are still running concurrently when they are not blocking the thread.
a
i skipped using coroutines altogether
how do you run coroutines in parallel on one thread?
that seems impossible
concurrent yes, parallel no
😮
b
Actor isn't deprecated. It's just being reworked for more complex scenarios which will make the existing APIs obsolete
I doubt they'll ever deprecate actors unless they find a reason to deprecate channels first
l
You're right, concurrent, not parallel.
a
I meant that the current API is obsolete so I can't use it without breaking my API after the come out with the new solution
b
They rarely release new APIs by removing all of the old ones if they can be made backwards compatible. Especially where the plan is to just expand the capabilities of actors, I see the current lambda APIs still being around for the simple cases