{"id":59159,"date":"2020-07-29T12:11:28","date_gmt":"2020-07-29T11:11:28","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=kotlin&#038;p=59159"},"modified":"2023-08-08T16:22:12","modified_gmt":"2023-08-08T15:22:12","slug":"kotlin-1-4-rc-debugging-coroutines","status":"publish","type":"kotlin","link":"https:\/\/blog.jetbrains.com\/en\/kotlin\/2020\/07\/kotlin-1-4-rc-debugging-coroutines","title":{"rendered":"Kotlin 1.4.0-RC: Debugging coroutines"},"content":{"rendered":"<p>We continue to highlight the upcoming changes in 1.4 release. In this blogpost, we want to describe a couple of important features related to coroutines:<\/p>\n<\/p>\n<ul>\n<li>New functionality to conveniently debug coroutines<\/li>\n<li>The ability to define deep recursive functions<\/li>\n<\/ul>\n<p>These changes are already available for you to try in the 1.4.0-RC release!<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/Kotlin-1.4-RC_banners_Blogpost.png\" width=\"800px\" alt=\"\" \/><\/p>\n<p>Let\u2019s dive into details.<\/p>\n<h2>Debugging coroutines<\/h2>\n<p>Coroutines are great for asynchronous programming (but not only for that), and many people already use them or are starting to use them. When you write code with coroutines, however, trying to debug them can be a real pain. Coroutines jump between threads. It can be difficult to understand what a specific coroutine is doing or to check its context. And in some cases, tracking steps over breakpoints simply doesn\u2019t work. As a result, you have to rely on logging or mental effort to debug the code with coroutines. To address this issue, we\u2019re introducing new functionality in the Kotlin plugin that aims to make debugging coroutines much more convenient.<\/p>\n<p>The <em>Debug Tool Window<\/em> now contains a new <em>Coroutines<\/em> tab. It is visible by default, and you can switch it on and off:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/image-1595615740412.png\" width=\"800px\" alt=\"\" \/><\/p>\n<p>In this tab, you can find information about both currently running and suspended coroutines. The coroutines are grouped by the dispatcher they are running on. If you started a coroutine with a custom name, you can find it by this name in the Tool Window. In the following example, you can see that the main coroutine is running (we\u2019ve stopped on a breakpoint inside it), and the other four coroutines are suspended:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/image-1595616002134.png\" width=\"600px\" alt=\"\" \/><\/p>\n<pre class=\"kotlin-code\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nimport kotlinx.coroutines.*\r\n\r\nfun main() = runBlocking {\r\n   repeat(4) {\r\n       launch(Dispatchers.Default + CoroutineName(&quot;Default-${&#039;a&#039; + it}&quot;)) {\r\n           delay(100)\r\n           val name = coroutineContext[CoroutineName.Key]?.name\r\n           println(&quot;I&#039;m &#039;$name&#039; coroutine&quot;)\r\n       }\r\n   }\r\n   delay(50)\r\n   \/\/ breakpoint\r\n   println(&quot;I&#039;m the main coroutine&quot;)\r\n}\r\n<\/pre>\n<p>With the new functionality, you can check the state of each coroutine and see the values of local and captured variables. This also works for suspended coroutines!<\/p>\n<p>In this example, we check the values of the local variables of suspended coroutines:<\/p>\n<pre class=\"kotlin-code\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nimport kotlinx.coroutines.*\r\n\r\nfun main() = runBlocking&lt;Unit&gt; {\r\n   launch {\r\n       val a = 3\r\n       delay(300)\r\n       println(a)\r\n   }\r\n   launch {\r\n       val b = 2\r\n       delay(200)\r\n       println(b)\r\n   }\r\n   launch {\r\n       val c = 1\r\n       delay(100)\r\n       \/\/ breakpoint here:\r\n       println(c)\r\n   }\r\n}\r\n<\/pre>\n<p>Choose a suspended coroutine (click <code>invokeSuspend<\/code> to see its state on that point) and the <code>Variables<\/code> tab will show you the state of the local variables:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/image-1595617185391.png\"\ndata-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/coroutines-debugger.gif\" \/><\/p>\n<p>You can now see a full coroutine creation stack, as well as a call stack inside the coroutine:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/image-1595615610849.png\" width=\"600px\" alt=\"\" \/><\/p>\n<p>Use the \u2018Get Coroutines Dump\u2019 option to get a full report containing the state of each coroutine and its stack:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/image-1595616044215.png\" width=\"600px\" alt=\"\" \/><\/p>\n<p>At the moment, the coroutines dump is still rather simple, but we\u2019re going to make it more readable and helpful in future versions.<\/p>\n<p>Note that to make the debugger stop at a given breakpoint inside a coroutine, this breakpoint should have the \u201cSuspend: All\u201d option chosen for it:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/image-1595616082379.png\" width=\"400px\" alt=\"\" \/><\/p>\n<p>To try this new functionality for debugging coroutines, you need to use the latest version of <a href=\"https:\/\/github.com\/Kotlin\/kotlinx.coroutines\" target=\"_blank\" rel=\"noopener\">kotlinx.coroutines<\/a>, <strong>1.3.8-1.4.0-rc<\/strong>, and the latest version of the Kotlin plugin (e.g. <strong>1.4.0-rc<\/strong>-release-IJ2020.1<strong>-2<\/strong>).<\/p>\n<p>The functionality is available only for Kotlin\/JVM. If you encounter any problems (please don\u2019t forget to share the details with us!), you can switch it off by opening <a href=\"https:\/\/www.jetbrains.com\/help\/idea\/kotlin-a.html\" target=\"_blank\" rel=\"noopener\">Build, Execution, Deployment | Debugger | Data Views | Kotlin<\/a> in <a href=\"https:\/\/www.jetbrains.com\/help\/idea\/settings-preferences-dialog.html\" target=\"_blank\" rel=\"noopener\">Preferences<\/a> and choosing <em>Disable coroutines agent<\/em>. For now, we\u2019re releasing this functionality for debugging coroutines in the <em>experimental<\/em> state, and we\u2019re looking forward to your feedback!<\/p>\n<h2>Defining deep recursive functions using coroutines<\/h2>\n<p>In Kotlin 1.4, you can define recursive functions and invoke them even when the call depth is greater than 100,000, using the standard library support based on coroutines!<\/p>\n<p>Let\u2019s first look at an ordinary recursive function, whose usage results in a <code>StackOverflowError<\/code> when the recursion depth gets too high. After that, we\u2019ll discuss how you can fix the problem and rewrite the function using the Kotlin standard library.<\/p>\n<p>We\u2019ll use a simple binary tree, where each <code>Tree<\/code> node has a reference to its <code>left<\/code> and <code>right<\/code> children:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nclass Tree(val left: Tree?, val right: Tree?)\r\n<\/pre>\n<p>The depth of the tree is the length of the longest path from its root to its child nodes. It can be computed using the following recursive function:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nfun depth(t: Tree?): Int =\r\n   if (t == null) 0 else maxOf(\r\n       depth(t.left),\r\n       depth(t.right)\r\n   ) + 1\r\n<\/pre>\n<p>The tree depth is the maximum of the depths of the left and right children increased by one. When the tree is empty it\u2019s zero.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/image-1595616140938.png\" width=\"200px\" alt=\"\"\/><\/p>\n<p>This function works fine when the recursion depth is small:<\/p>\n<pre class=\"kotlin-code\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nclass Tree(val left: Tree?, val right: Tree?)\r\n\r\nfun depth(t: Tree?): Int =\r\n   if (t == null) 0 else maxOf(\r\n       depth(t.left),\r\n       depth(t.right)\r\n   ) + 1\r\n\r\nfun main() {\r\n\/\/sampleStart\r\n    val tree = Tree(Tree(Tree(null, null), null), null)\r\n    println(depth(tree)) \/\/ 3\r\n\/\/sampleEnd\r\n}\r\n<\/pre>\n<p>However, if you create a tree with a depth greater than 100,000, which in practice is not so uncommon, you\u2019ll get <code>StackOverflowError<\/code> as a result:<\/p>\n<pre class=\"kotlin-code\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nclass Tree(val left: Tree?, val right: Tree?)\r\n\r\nfun depth(t: Tree?): Int =\r\n   if (t == null) 0 else maxOf(\r\n       depth(t.left),\r\n       depth(t.right)\r\n   ) + 1\r\n\/\/sampleStart\r\nfun main() {\r\n   val n = 100_000\r\n   val deepTree = generateSequence(Tree(null, null)) { prev -&gt;\r\n       Tree(prev, null)\r\n   }.take(n).last()\r\n\r\n   println(depth(deepTree))\r\n}\r\n\/*\r\nException in thread &quot;main&quot; java.lang.StackOverflowError\r\n  at FileKt.depth(File.kt:5)\r\n  ...\r\n*\/\r\n\/\/sampleEnd\r\n<\/pre>\n<p>The problem is that the call stack gets too large. To solve this issue, you can use a VM option to increase the maximum stack size. However, while this might work for specific use cases, it\u2019s not a practical solution for the general case.<\/p>\n<p>Alternatively, you can rewrite the code and store results for intermediate calls by hand in the heap rather than on the stack. This solution works in most cases and is common in other languages. However, the resulting code becomes non-trivial and complicated, and the beauty and simplicity of the initial function are lost. You can find an example <a href=\"https:\/\/pl.kotl.in\/HYlBs521n\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>Kotlin now provides a clean way to solve this problem based on the coroutines machinery.<\/p>\n<p>The Kotlin library now includes the definition <code>DeepRecursiveFunction<\/code>, which models recursive calls using the suspension mechanism:<\/p>\n<pre class=\"kotlin-code\" data-version=\"1.4-M3\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nclass Tree(val left: Tree?, val right: Tree?)\r\n\r\n@OptIn(ExperimentalStdlibApi::class)\r\nval depthFunction = DeepRecursiveFunction&lt;Tree?, Int&gt; { t -&gt;\r\n   if (t == null) 0 else maxOf(\r\n       callRecursive(t.left),\r\n       callRecursive(t.right)\r\n   ) + 1\r\n}\r\n\r\n@OptIn(ExperimentalStdlibApi::class)\r\nfun depth(t: Tree) = depthFunction(t)\r\n\r\nfun main() {\r\n   val n = 100_000\r\n   val deepTree = generateSequence(Tree(null, null)) { prev -&gt;\r\n       Tree(prev, null)\r\n   }.take(n).last()\r\n\r\n   println(depth(deepTree)) \/\/ 100000\r\n}\r\n<\/pre>\n<p>You can compare the two versions, the initial one and the one using <code>DeepRecursiveFunction<\/code>, to make sure that the logic remains the same. Your new function now becomes a variable of type <code>DeepRecursiveFunction<\/code>, which you can call using the <a href=\"https:\/\/kotlinlang.org\/docs\/reference\/operator-overloading.html#invoke\" target=\"_blank\" rel=\"noopener\">&#8216;invoke&#8217; convention<\/a> as <code>depthFunction(t)<\/code>. The function body now becomes the body of the lambda argument of <code>DeepRecursiveFunction<\/code>, and the recursive call is replaced with <code>callRecursive<\/code>. These changes are straightforward and easy to make. Note that while the new <code>depth<\/code> function uses coroutines under the hood, it is not itself a <code>suspend<\/code> function.<\/p>\n<p>Understanding how <code>DeepRecursiveFunction<\/code> is implemented is interesting, but it is not necessary in order for you to use it and benefit from it. You can find the implementation details described in <a href=\"https:\/\/medium.com\/@elizarov\/deep-recursion-with-coroutines-7c53e15993e3\" target=\"_blank\" rel=\"noopener\">this blog post<\/a>.<\/p>\n<p><code>DeepRecursiveFunction<\/code> is a part of the Kotlin standard library, not part of the <code>kotlinx.coroutines<\/code> library, since it\u2019s not about asynchronous programming. At the moment this API is still experimental, so we\u2019re looking forward to your feedback!<\/p>\n<h2 id=\"how-to-try\">How to try it<\/h2>\n<p>As always, you can <strong>try Kotlin online<\/strong> at<a href=\"http:\/\/play.kotl.in\/\" target=\"_blank\" rel=\"noopener\"> play.kotl.in<\/a>.<\/p>\n<p>In <strong>IntelliJ IDEA<\/strong>, you can update the Kotlin Plugin to version 1.4.0-RC. See<a href=\"https:\/\/kotlinlang.org\/eap\/install-eap-plugin.html\" target=\"_blank\" rel=\"noopener\"> how to do this<\/a>.<\/p>\n<p>If you want to work on existing projects that were created before installing the preview version, you need to<a href=\"https:\/\/kotlinlang.org\/eap\/configure-build-for-eap.html\" target=\"_blank\" rel=\"noopener\"> configure your build for the preview version in Gradle or Maven<\/a>. Note that unlike the previous preview versions, Kotlin 1.4.0-RC is also available directly from Maven Central. This means you won\u2019t have to manually add the <code>kotlin-eap<\/code> repository to your build files. <\/p>\n<p>You can download <strong>the command-line compiler<\/strong> from the<a href=\"https:\/\/github.com\/JetBrains\/kotlin\/releases\/tag\/v1.4-M1\" target=\"_blank\" rel=\"noopener\"> Github release page<\/a>.<\/p>\n<h2 id=\"share-your-feedback\">Share your feedback<\/h2>\n<p>We\u2019ll be very thankful if you find and report bugs to our <a href=\"https:\/\/youtrack.jetbrains.com\/issues\/KT\" target=\"_blank\" rel=\"noopener\">issue tracker<\/a>. We\u2019ll try to fix all the important issues before the final release, which means you won\u2019t need to wait until the next Kotlin release for your issues to be addressed.<\/p>\n<p>You are also welcome to join the <em><a href=\"https:\/\/app.slack.com\/client\/T09229ZC6\/C0KLZSCHF\" target=\"_blank\" rel=\"noopener\">#eap channel<\/a><\/em> in<a href=\"https:\/\/app.slack.com\/client\/T09229ZC6\/C0KLZSCHF\" target=\"_blank\" rel=\"noopener\"> Kotlin Slack<\/a> (get an invite<a href=\"http:\/\/slack.kotlinlang.org\/\" target=\"_blank\" rel=\"noopener\"> here<\/a>). In this channel, you can ask questions, participate in discussions, and get notifications about new preview builds.<\/p>\n<p>Let\u2019s Kotlin!<\/p>\n","protected":false},"author":42,"featured_media":59243,"comment_status":"open","ping_status":"closed","template":"","categories":[907],"tags":[91],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/kotlin\/59159"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/kotlin"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/kotlin"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=59159"}],"version-history":[{"count":2,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/kotlin\/59159\/revisions"}],"predecessor-version":[{"id":139457,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/kotlin\/59159\/revisions\/139457"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media\/59243"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=59159"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=59159"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=59159"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=59159"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}