<div dir="ltr"><div>Mike,</div><div><br></div><div>Thanks so much for teaching us some things.<br></div><div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Jul 19, 2018 at 1:20 PM, Michael Comella <span dir="ltr"><<a href="mailto:mcomella@mozilla.com" target="_blank">mcomella@mozilla.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>Thanks for the updates, Nick – it's interesting to hear what other teams are doing and how it may affect me. :) One quick thought:<br></div><span class=""><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Rust code should be synchronous</blockquote><div><br></div></span><div>I haven't thought about this enough to have an opinion on whether this is the right decision or not. However, this may encourage the Rust library consumers to make a mistake I once made with Kotlin coroutines: <b>I want to share so others don't make the same mistake.</b></div><div><br></div><div>While you can create an obscenely large number of coroutines, there is a limit to the number that can run concurrently: this limit is defined by the thread pool they run on. The default built-in `CommonPool` has a fixed number of threads. If you run blocking code (e.g. potentially synchronous Rust code ^) on this thread pool, i<b>t prevents other coroutines from beginning to run</b> until the blocking code completes. <a href="https://github.com/mcomella/BlockingCoroutinesExample" target="_blank">Here's a demonstration using Android.</a></div></div></blockquote><div><br></div><div>I was concerned about this, for slightly different reasons: I care about the same thread of execution servicing the next FFI call, i.e., that FFI calls don't rotate between threads non-deterministically.  But I think it's very similar to the situation you warn about.  I took an action item to read more about Kotlin co-routines, and summarized (in the notes) like this:</div><div><br></div><div><ul style="margin-top:0pt;margin-bottom:0pt" id="gmail-docs-internal-guid-a6c72c1f-b88e-7449-9fa4-2a5bf7bcc40e"><ul style="margin-top:0pt;margin-bottom:0pt"><ul style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:square;font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">AI: </span><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">nalexander to read more</span><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> about how Kotlin coroutines are implemented</span></p></li><ul style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:disc;font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">[nalexander] </span><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">OK, I’ve done this.</span><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">  Based on the </span><a href="https://kotlinlang.org/docs/reference/coroutines.html" style="text-decoration:none"><span style="font-size:11pt;font-family:"Open Sans";color:rgb(17,85,204);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:underline;vertical-align:baseline;white-space:pre-wrap">Kotlin 1.1+ coroutines documentation</span></a><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">, Kotlin coroutines are implemented as a CPS transformation and an internal state machine.  That state machine is pumped from a single thread.  That’s very similar to </span><a href="https://github.com/clojure/core.async" style="text-decoration:none"><span style="font-size:11pt;font-family:"Open Sans";color:rgb(17,85,204);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:underline;vertical-align:baseline;white-space:pre-wrap">clojure/core.async</span></a><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> and means that there’s no thread pool servicing coroutines!  Any additional thread must be the result of the application (or a library).  </span><span style="font-size:11pt;font-family:"Open Sans";color:rgb(0,0,0);background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">That means that all my hypothetical concerns about coroutines invoking FFI methods on multiple threads are not justified.</span></p></li></ul></ul></ul></ul></div><div>Am I incorrect?  Please correct me if so!<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>Why is this a problem? <b>If you're a library (e.g. android-components) that uses the same fixed thread pool as the application, you may block the application's coroutines from starting quickly!</b> For example, if the library makes several calls to read configuration files from disk, each on a new coroutine on the CommonPool, these may block all of the CommonPool threads. If all the threads are blocked and the application spawns a coroutine on the CommonPool to do background processing before rendering the results in the UI, the application's coroutine (and thus the UI update) will wait until the library's coroutines finish blocking before it runs.</div><div><br></div><div>---<br></div><div><br></div><div>Some possible solutions to this are:</div><div>- Use a separate thread pool for blocking calls (here's an <a href="https://github.com/Kotlin/kotlinx.coroutines/issues/79" target="_blank">open ticket for a system-wide IO thread pool</a>)</div><div>- Use non-blocking IO (<a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-nio/" target="_blank">supported in Java 7+ with nio</a>)</div></div></blockquote><div><br></div></div><div class="gmail_quote">I agree that `CommonPool` can cause issues.  (Just for the record, I had not heard of `CommonPool`.)  But to me, those issues are clearly _above_ the Rust FFI layer, and the _common_ part of the name is pretty clear that applications and libraries need to coordinate in order to prevent bad things (in this case, limiting throughput due to resource starvation, or, in the worst case, livelocking).</div><div class="gmail_quote"><br></div><div class="gmail_quote">Coming back to my summary of how Kotlin coroutines are implemented, something above the Rust library layer has to handle adding asynchrony to the synchronous Rust -- either by spawning threads (with a pool, common or otherwise, if desired); or by doing non-blocking things where possible; or ...</div><div class="gmail_quote"><br></div><div class="gmail_quote">Are we in accord?<br></div><div class="gmail_quote">Nick</div><div class="gmail_quote"><br></div></div></div></div>