13. References Locations Contents
resolve download
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
val content = downloadContent(location)
processContent(ref, content)
}
}
14. References Locations Contents
resolve download
suspend fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
val content = downloadContent(location)
processContent(ref, content)
}
}
15. References Locations Contents
resolve download
suspend fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
val content = downloadContent(location)
processContent(ref, content)
}
}
Sequential
16. References Locations Contents
resolve download
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
val content = downloadContent(location)
processContent(ref, content)
}
}
Parallel
17. References Locations Contents
resolve download
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
GlobalScope.launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
Parallel
18. fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
GlobalScope.launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
Coroutines are cheap! What could go wrong?
19. fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
GlobalScope.launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
ref1 location1 Launch download
ref2 location2 Launch download
ref3
Crash!
Crash!
20. ref1 location1 Launch download
ref2 location2 Launch download
ref3
Crash!
Crash! Leak!
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
GlobalScope.launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
25. suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
GlobalScope.launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
26. suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
27. suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
Child
28. Crash?
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
29. suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
Crash?
30. suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
Crash?
cancels
31. Crash?
cancels
Waits for completion
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
32. suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLocation()
launch {
val content = downloadContent(location)
processContent(ref, content)
}
}
}
Never leaks jobs
38. class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location = ref.resolveLocation()
…
}
}
39. class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
}
}
40. class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
41. class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
Concurrent
42. class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
Concurrent
43. class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
Shared mutable state
44. class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
Shared mutable state
Needs Synchronization
Shared + Mutable =
49. launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
50. launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
56. fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
57. fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
58. fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
59. fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
// schedule download
}
// ... wait for result ...
processContent(ref, content)
}
}
60. fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
launch { … }
}
// ... wait for result ...
processContent(ref, content)
}
}
Coroutines are cheap! What could go wrong?
61. fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
launch { … }
}
// ... wait for result ...
processContent(ref, content)
}
}
Child
62. fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (requested.add(location)) {
launch { … }
}
// ... wait for result ...
processContent(ref, content)
}
}
Coroutines are cheap! But the work they do…
77. data class LocContent(val loc: Location, val content: Content)
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>,
contents: SendChannel<LocContent>
)
78. data class LocContent(val loc: Location, val content: Content)
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>,
contents: SendChannel<LocContent>
) =
launch {
for (loc in locations) {
val content = downloadContent(loc)
contents.send(LocContent(loc, content))
}
}
123. suspend fun downloadContent(location: Location): Content
fun CoroutineScope.processReferences(…)
Launches new coroutines &
quickly returns, does not wait for them