https://kotlinlang.org logo
#coroutines
Title
# coroutines
z

Zach Klippenstein (he/him) [MOD]

04/26/2021, 11:30 PM
GlobalScope
is getting guard rails! My dream come true 😌
🎉 2
r

Robert Jaros

04/27/2021, 12:10 AM
I know
GlobalScope
usage is discouraged, but I think it's hard to avoid it when developing on Kotlin/JS. Most of the time there is just no better/easier way to launch coroutines. Of course I can create another singleton object instead, but for me it just doesn't make sense.
1
b

baxter

04/27/2021, 4:04 AM
🤣 congrats! Honest question regarding
GlobalScope
, while I agree that it should never be used, would it be better to use a new scope that is never canceled for tasks that need to run once in a coroutine, or run it in the global scope? For example, on an Android project, I have a specific job I want to run once, and don't need any result back. It can take the entire app process lifecycle (if it is short) or run until completion.
I leaned toward a new scope at first, but then started leaning for using the global scope, since that new scope object was a one-shot object already. This task was a new thread beforehand, but now runs in the IO dispatcher (it's IO work), just to give some context.
z

Zach Klippenstein (he/him) [MOD]

04/27/2021, 4:28 AM
Firstly, even if you are using
GlobalScope
, you shouldn’t hard-code it – you should inject it and provide it from a module near the root of your DI graph. Then, it’s still not a bad idea to make your own scope, because you can set the dispatcher explicitly, instead of having to remember what dispatcher
GlobalScope
happens to use.
☝️ 1
l

louiscad

04/27/2021, 6:10 AM
I have an
AppScope
in real world projects
2
j

Jan Skrasek

04/27/2021, 6:51 AM
Firstly, even if you are using 
GlobalScope
, you shouldn’t hard-code it – you should inject it and provide it from a module near the root of your DI graph.
Why so? Isn't injecting a dispatcher sufficient?
b

baxter

04/27/2021, 6:56 AM
These are some good suggestions. I am iffy on adding GlobalScope to the DI graph, as I can see that being abused. We are pushing to ensure all scopes are created with the main dispatcher, so having one that isn't would break trust. Having an AppScope that follows our paradigm in the DI graph might be the better option, but then the same lifecycle problems with global scope would exist in this app scope... Might revisit some code tomorrow to see what would become our best practice for this.
l

louiscad

04/27/2021, 7:58 AM
If your
AppScope
has a
Job
inside, it's safer than
GlobalScope
.
☝️ 2
🙏 1
z

Zach Klippenstein (he/him) [MOD]

04/27/2021, 12:48 PM
Yea, I wouldn’t use global scope even when it’s injected (your example, using the main dispatcher for all coroutines by default instead of Default, is a great reason), but the point is that if it is injected, none of your code actually has to know or care what that root coroutine scope actually is, and changing it to a different scope is trivial.
1
Why so? Isn’t injecting a dispatcher sufficient?
It’s better than nothing, but injecting a context/scope also lets you customize other things at the root. Eg: • In a lot of the main production apps I work on, we actually keep the process alive across independent instrumentation tests and for that to work you need everything that is scoped to the “app” to actually be disposable/cancellable. • Adding logging or debug interceptors
1
🙏 1
a

andylamax

04/28/2021, 10:54 AM
@Robert Jaros I am actively developing in Kotlin/JS (browser) and I never have to use
GlobalScope
at all, I just
Copy code
CoroutineScope(window.asCoroutineDispatcher()).launch {
  // do things here
}
I think there is a Kotlin/JS(node) way of getting a dispatcher scoped to the process
r

Robert Jaros

04/28/2021, 11:27 AM
Is there any significant difference between using above and the
GlobalScope
on JS?
j

Jan Skrasek

04/28/2021, 11:28 AM
What does "safer" mean?
l

louiscad

04/28/2021, 11:29 AM
r

Robert Jaros

04/28/2021, 11:40 AM
Do I understand correctly that we should always keep the reference to the CoroutineScope?
l

louiscad

04/28/2021, 11:41 AM
Yes
Top-level, or class instance scoped works.
r

Robert Jaros

04/28/2021, 11:42 AM
So the way proposed by @andylamax (
CoroutineScope(window.asCoroutineDispatcher()).launch { }
) is still not safe enough?
l

louiscad

04/28/2021, 11:44 AM
You need to keep a reference to the
CoroutineScope
or to the
Job
returned by
launch
or it will be garbage collected whenever there's no strong reference held and the coroutine is suspended.
Usually, it doesn't happen because whatever callback is waiting keeps a strong reference to the continuation, but there are widely used callback APIs that use WeakReferences internally. For example. SharedPreferences and MediaPlayer on Android.
In those cases, no strong ref to your listener, which was the only one holding a ref to the continuation. If GC passes during that potentially very long timeframe, the coroutine goes away silently and you wonder why something is not happening or is not working.
Actually, I think you're making a case to submit a feature request for an IDE inspection that puts a red warning when you create a
CoroutineScope
but don't put it in a property.
1
Want to submit it on kotl.in/issue @Robert Jaros?
r

Robert Jaros

04/28/2021, 11:54 AM
I think it should be more clearly stated in the documentation here: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/, because it doesn't mention the GC problem.
l

louiscad

04/28/2021, 11:54 AM
That'd be an issue to raise on GitHub
Probably there should be a troubleshooting specific page in the doc.
r

Robert Jaros

04/28/2021, 11:59 AM
With
GlobalScope
"gone" 😉 the documentation on creating custom scopes the right way (with all this important information) should be somewhere in the main docs here https://github.com/Kotlin/kotlinx.coroutines
a

andylamax

04/28/2021, 12:23 PM
So, for
JS
something like
Copy code
val scope = CoroutineScope(window.asCoroutineDispatcher())

scope.launch {
  // do stuff here
}
works?
What a polite way to say, "I told you so"😂
😅 2
b

baxter

04/28/2021, 5:59 PM
This thread has been great insight on some of the minor nuances of Coroutines, especially when it comes to using
GlobalScope
. Thanks @louiscad for the insight on the ticket and explanation in ensuring a strong reference to a job is kept.
🙂 2
Working with my team to update our best practice to avoid GlobalScope at all costs (instead of having really good reasons). Going with an
AppScope
object now.
👍 2
🙌 2
u

ursus

04/30/2021, 1:13 AM
What about injecting scope vs. letting the class own its scope?
Copy code
@AppScope
1. class MessageSender() : HasLifecycle {
	private val scope = SuperviserScope(...)

	fun sendMessage() {
		scope.launch {
			doSendMessage()
		}
	}

	private suspend fun doSendMessage() {
		...
	}

	override fun destroy() {
		scope.cancel()
	}
}

@AppScope
2. class MessageSender(scope: coroutineScope) {

	fun sendMessage() {
		scope.launch {
			doSendMessage()
		}
	}

	private suspend fun doSendMessage() {
		...
	}
}
any reasons for one over the other?
l

louiscad

04/30/2021, 10:17 AM
It's not the topic of this thread FYI.
6 Views