This repository is the code corresponding to the Introduction to Coroutines and Channels Hands-On Lab.
kotlin-hands-on / intro-coroutines Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
This repository is the code corresponding to the Introduction to Coroutines and Channels Hands-On Lab.
Request5ConcurrentKtTest.kt fails for me with:
java.lang.IllegalStateException: This job has not completed yet
Maybe this gets fixed once
kotlin version used: 1.3.41
https://github.com/kotlin-hands-on/intro-coroutines/blob/solutions/src/tasks/Request5Concurrent.kt .
and
https://play.kotlinlang.org/hands-on/Introduction%20to%20Coroutines%20and%20Channels/05_Concurrency (section solution)
The documentation does not have service as a parameter, code and solution do.
It seems that all the chapters of the documentation have the same issue.
In the rx
branch, Request9RxProgressKtTest
is failing due to AssertionError
.
It seems the test is failing due to the improvement made in loadContributorsReactiveProgress
function by using Observable.scan()
function instead of Observable.map()
as highlighted below:
return repoUsers
.scan(listOf()) { allUsers, users ->
(allUsers + users).aggregate()
}
The problem here is scan()
function emits the initialValue
while subscribing, in this case, empty list and hence the test assertion is failing.
There are two ways to solve this:
map()
instead of scan()
filter()
to filter out the empty list after scan()
I think the second one is kind of confusing, so can go ahead with first. Thoughts?
I've noticed that the fastest coroutine variant runs in a ~5.6 seconds
however, the RxJava variant runs in ~2 seconds,
If I understand correctly,
RxJava runs every request on a different thread and holds it while the coroutine uses a looper to schedule all of the io on the same thread, saving space.
I've tried achieving better performance on coroutines to be in par with RxJava but failed,
can you better explain that? it seems to me that the RxJava variant is much better because of that (for time optimization).
In contributors.MockGithubService
in comment section it needs Call
to be appended to the end of each method. I cant push branch with such fix :(
@svtk
I am unable to pinpoint the problem here but after importing the project into IntelliJ from GitHub I am running into the following error on execution of src/contributors/main/kt
Exception in thread "main" java.awt.AWTError: Assistive Technology not found: org.GNOME.Accessibility.AtkWrapper
at java.awt.Toolkit.loadAssistiveTechnologies(Toolkit.java:807)
at java.awt.Toolkit.getDefaultToolkit(Toolkit.java:886)
at sun.swing.SwingUtilities2.getSystemMnemonicKeyMask(SwingUtilities2.java:2032)
at javax.swing.plaf.basic.BasicLookAndFeel.initComponentDefaults(BasicLookAndFeel.java:1158)
at javax.swing.plaf.metal.MetalLookAndFeel.initComponentDefaults(MetalLookAndFeel.java:431)
at javax.swing.plaf.basic.BasicLookAndFeel.getDefaults(BasicLookAndFeel.java:148)
at javax.swing.plaf.metal.MetalLookAndFeel.getDefaults(MetalLookAndFeel.java:1577)
at javax.swing.UIManager.setLookAndFeel(UIManager.java:539)
at javax.swing.UIManager.setLookAndFeel(UIManager.java:579)
at javax.swing.UIManager.initializeDefaultLAF(UIManager.java:1349)
at javax.swing.UIManager.initialize(UIManager.java:1459)
at javax.swing.UIManager.maybeInitialize(UIManager.java:1426)
at javax.swing.UIManager.getLookAndFeelDefaults(UIManager.java:1037)
at contributors.ContributorsUIKt.setDefaultFontSize(ContributorsUI.kt:147)
at contributors.MainKt.main(main.kt:4)
at contributors.MainKt.main(main.kt)
Thanks in advance.
Only blocking and background variant works well
90 [AWT-EventQueue-0] INFO Contributors - Clearing result
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by retrofit2.Platform (file:/C:/Users/Gunnar/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.9.0/d8fdfbd5da952141a665a403348b74538efc05ff/retrofit-2.9.0.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int)
WARNING: Please consider reporting this to the maintainers of retrofit2.Platform
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Exception in thread "AWT-EventQueue-0" kotlin.NotImplementedError: An operation is not implemented.
at tasks.Request4SuspendKt.loadContributorsSuspend(Request4Suspend.kt:6)
at contributors.Contributors$loadContributors$3.invokeSuspend(Contributors.kt:77)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
started with:
"C:\Program Files\OpenJDK11\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition Eval\lib\idea_rt.jar=64163:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition Eval\bin" -Dfile.encoding=UTF-8 -classpath "C:\Development\Travelling Salesman\GitHub\KotlinIntroCoroutines\build\classes\kotlin\main;C:\Development\Travelling Salesman\GitHub\KotlinIntroCoroutines\build\resources\main;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-reflect\1.5.20\63810951f09a27f7827977f1bc21094042a915e\kotlin-reflect-1.5.20.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\com.jakewharton.retrofit\retrofit2-kotlinx-serialization-converter\0.8.0\4fe25acf1b2f12f17d7187f7cfcf4ee730822d50\retrofit2-kotlinx-serialization-converter-0.8.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\com.squareup.retrofit2\retrofit-mock\2.9.0\da17c2bcd6a771694ce79091d8394a3118bd7890\retrofit-mock-2.9.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\com.squareup.retrofit2\adapter-rxjava2\2.9.0\180ad7353dbf0480cabc215948b5a234e3692f0a\adapter-rxjava2-2.9.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\com.squareup.retrofit2\retrofit\2.9.0\d8fdfbd5da952141a665a403348b74538efc05ff\retrofit-2.9.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\com.squareup.okhttp3\okhttp\4.9.1\51215279c3fe472c59b6b7dd7491e6ac2e28a81b\okhttp-4.9.1.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-swing\1.5.1\3a6498130368bbd3a5cdbb15264e64cedac80eb5\kotlinx-coroutines-swing-1.5.1.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-jdk8\1.5.1\6a30b64389d2f541d27c7756d414412eb52a15d\kotlinx-coroutines-jdk8-1.5.1.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-slf4j\1.5.1\df3c9da192aa8c14da5f1b81e0c39e8fb646cc10\kotlinx-coroutines-slf4j-1.5.1.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib\1.5.20\9de35cc611bcecec8edce1d56d8e659953806751\kotlin-stdlib-1.5.20.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-debug\1.5.1\f0e934de961d0d36f3fb9c61114ddf8803abbcde\kotlinx-coroutines-debug-1.5.1.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\ch.qos.logback\logback-classic\1.2.3\7c4f3c474fb2c041d8028740440937705ebb473a\logback-classic-1.2.3.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\io.reactivex.rxjava2\rxkotlin\2.4.0\2f4a35d683b4851a036b8828d4a7c6888adf75c7\rxkotlin-2.4.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\io.reactivex.rxjava2\rxjava\2.2.21\6f13f24c44567fc660aab94d639cda9f0fe95628\rxjava-2.2.21.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-serialization-core-jvm\1.2.2\f1facb8403cacd65ee314f91fec216ba229f9ac2\kotlinx-serialization-core-jvm-1.2.2.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.reactivestreams\reactive-streams\1.0.3\d9fb7a7926ffa635b3dcaa5049fb2bfa25b3e7d0\reactive-streams-1.0.3.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\com.squareup.okio\okio\2.8.0\49b64e09d81c0cc84b267edd0c2fd7df5a64c78c\okio-jvm-2.8.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-serialization-json-jvm\1.2.2\7cd520a077c5d1addfa8f80f39cd14cb2de027d7\kotlinx-serialization-json-jvm-1.2.2.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-jdk8\1.5.20\636c5653641cd956de9aee5792155b07ea49825e\kotlin-stdlib-jdk8-1.5.20.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.slf4j\slf4j-api\1.7.25\da76ca59f6a57ee3102f8f9bd9cee742973efa8a\slf4j-api-1.7.25.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-core-jvm\1.5.1\59d09c330d2977b39e1e4c0e3711fc903e1b46b0\kotlinx-coroutines-core-jvm-1.5.1.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains\annotations\13.0\919f0dfe192fb4e063e7dacadee7f8bb9a2672a9\annotations-13.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-common\1.5.20\3d79dbd48bf605f4aac1e7028981a1953e245cbb\kotlin-stdlib-common-1.5.20.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\ch.qos.logback\logback-core\1.2.3\864344400c3d4d92dfeb0a305dc87d953677c03c\logback-core-1.2.3.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-jdk7\1.5.20\218b60e1d446d1e0a18bc7aa8663634b136fbcc5\kotlin-stdlib-jdk7-1.5.20.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\net.java.dev.jna\jna-platform\5.5.0\af38e7c4d0fc73c23ecd785443705bfdee5b90bf\jna-platform-5.5.0.jar;C:\Users\Gunnar.gradle\caches\modules-2\files-2.1\net.java.dev.jna\jna\5.5.0\e0845217c4907822403912ad6828d8e0b256208\jna-5.5.0.jar" contributors.MainKt
Request6ProgressKtTest.testProgress failed with this:
Wrong intermediate result after 3200: expected:<[User(login=user-2, contributions=50), User(login=user-1, contributions=50)]> but was:<[User(login=user-1, contributions=50), User(login=user-2, contributions=50)]>
Expected :[User(login=user-2, contributions=50), User(login=user-1, contributions=50)]
Actual :[User(login=user-1, contributions=50), User(login=user-2, contributions=50)]
intro-coroutines/src/tasks/Aggregation.kt
Line 20 in c51a403
.sortedWith(compareByDescending<User> { it.contributions }
.thenByDescending { it.login })
This is my solution for Request6Progress.kt:
suspend fun loadContributorsProgress(
service: GitHubService,
req: RequestData,
updateResults: suspend (List<User>, completed: Boolean) -> Unit
) {
val repos = service
.getOrgRepos(req.org)
.also { logRepos(req, it) }
.body() ?: listOf()
var allUsers = emptyList<User>()
repos.forEachIndexed { index, repo ->
val users = service
.getRepoContributors(req.org, repo.name)
.also { logUsers(repo, it) }
.bodyList()
allUsers += users
// allUsers = (allUsers + users).aggregate() // uncomment this line to make the test happy
updateResults(allUsers.aggregate(), index == repos.lastIndex)
}
}
As far as I understand the current revision of Using callbacks suggests that using CountDownLatch is the best approach to solve the problem.
However, the provided solution doesn't work even if one of the requests fails. When a request fails, the error is simply logged on Request3Callbacks.kt#L34. countDown() isn't called in that case. Because of that, the thread will never return from await() call.
The same goes for the second suggested solution (AtomicInteger). The counter isn't incremented if a request fails. Because of that, when all requests are completed, the counter won't be equal to the count of repositories.
I am not sure if this should be fixed or even mentioned in the docs, but I think it should be at least listed as a known issue.
Dear JetBrains
.apply() seems to be deprecated and I have problems running the app in Intellij since I can't find the main class.
I cloned the project and run the main method in contributors/main.kt. Nothing happens. Seems like some plugin is missing. But how do I add that?
Just cloned it and run gradle build:
Build file '/Users/adrianalbu/Downloads/intro-coroutines/build.gradle' line: 2
Plugin [id: 'org.jetbrains.kotlin.jvm', version: '1.5.20'] was not found in any of the following sources:
Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.
Exception is:
org.gradle.api.plugins.UnknownPluginException: Plugin [id: 'org.jetbrains.kotlin.jvm', version: '1.5.20'] was not found in any of the following sources:
Kotlin doc says the cloned project will include a 'solutions' branch.
But I see it nowhere.
To fix the callback solution I suggest to use a CountDownLatch instead of an AtomicInteger based solution. It won't save LOC but allows for a more decoupled solution (updating the results isn't part of the backend call code):
val countDownLatch = CountDownLatch(repos.size)
for (repo in repos) {
service.getRepoContributorsCall(req.org, repo.name).onResponse { responseUsers ->
// more code here
countDownLatch.countDown()
}
}
countDownLatch.await()
updateResults(allUsers.aggregate())
In Request3Callbacks.kt you use AtomicInteger to make sure you increment and read the integer in a thread safe way, but you append to the allUsers list which isn't synchronized. Isn't this a problem?
I think it needs a synchronized block
fun loadContributorsCallbacks(service: GitHubService, req: RequestData, updateResults: (List<User>) -> Unit) {
service.getOrgReposCall(req.org).onResponse { responseRepos ->
logRepos(req, responseRepos)
val repos = responseRepos.bodyList()
val allUsers = mutableListOf<User>()
var numberOfProcessed = 0
val lock = Any()
for (repo in repos) {
service.getRepoContributorsCall(req.org, repo.name).onResponse { responseUsers ->
synchronized(lock) {
logUsers(repo, responseUsers)
val users = responseUsers.bodyList()
allUsers += users
numberOfProcessed += 1
if (numberOfProcessed == repos.size) {
updateResults(allUsers.aggregate())
}
}
}
}
}
}
It has been said to avoid runBlocking and it would never really be used in production code. So, can the examples be more relevant to an existing project where you have Kotlin code and use a new library that has coroutines. Most likely you would not want runBlocking right?
Hello 😃
In part of tutorial "Canceling the loading of contributors" on step 3 it is suggested to add 3-second delay.
And the actual code sample stands like that:
suspend fun loadContributorsConcurrent(
service: GitHubService,
req: RequestData
): List<User> = coroutineScope {
// ...
GlobalScope.async {
log("starting loading for ${repo.name}")
delay(3000)
// load repo contributors
}
// ...
}
I guess, it shouldn't be GlobalScope.async {}
in this case. Therefore, if we will add GlobalScope
, then loading won't be canceled.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.