{"id":627,"date":"2018-04-23T10:46:32","date_gmt":"2018-04-23T08:46:32","guid":{"rendered":"https:\/\/coaxion.net\/blog\/?p=627"},"modified":"2018-04-25T09:08:20","modified_gmt":"2018-04-25T07:08:20","slug":"glib-gio-async-operations-and-rust-futures-async-await","status":"publish","type":"post","link":"https:\/\/coaxion.net\/blog\/2018\/04\/glib-gio-async-operations-and-rust-futures-async-await\/","title":{"rendered":"GLib\/GIO async operations and Rust futures + async\/await"},"content":{"rendered":"<p>Unfortunately I was not able to attend the <a href=\"https:\/\/wiki.gnome.org\/Hackfests\/Rust2018\" rel=\"noopener\" target=\"_blank\">Rust+GNOME hackfest<\/a> in Madrid last week, but I could at least spend some of my work time at <a href=\"https:\/\/centricular.com\/\" rel=\"noopener\" target=\"_blank\">Centricular<\/a> on implementing one of the things I wanted to work on during the hackfest. The other one, more closely related to the <a href=\"https:\/\/blog.guillaume-gomez.fr\/articles\/2018-04-21+Rust%2BGNOME+Hackfest+in+Madrid\" rel=\"noopener\" target=\"_blank\">gnome-class work<\/a>, will be the topic of a future blog post once I actually have something to show.<\/p>\n<p>So back to the topic. With the latest GIT version of the <a href=\"https:\/\/www.rust-lang.org\/en-US\/\" rel=\"noopener\" target=\"_blank\">Rust<\/a> bindings for <a href=\"http:\/\/gtk-rs.org\/\" rel=\"noopener\" target=\"_blank\">GLib, GTK, etc<\/a> it is now possible to make use of the Rust futures infrastructure for GIO async operations and various other functions. This should make writing of <a href=\"https:\/\/www.gnome.org\/\" rel=\"noopener\" target=\"_blank\">GNOME<\/a>, and in general GLib-using, applications in Rust quite a bit more convenient.<\/p>\n<p>For the impatient, the summary is that you can use Rust futures with GLib and GIO now, that it works both on the stable and nightly version of the compiler, and with the nightly version of the compiler it is also possible to use async\/await. An example with the latter can be found <a href=\"https:\/\/github.com\/gtk-rs\/examples\/blob\/a684d90e4233d6eeab8aff5e65013cd9208b409f\/src\/bin\/gio_futures_await.rs\" rel=\"noopener\" target=\"_blank\">here<\/a>, and an example just using futures without async\/await <a href=\"https:\/\/github.com\/gtk-rs\/examples\/blob\/a684d90e4233d6eeab8aff5e65013cd9208b409f\/src\/bin\/gio_futures.rs\" rel=\"noopener\" target=\"_blank\">here<\/a>.<\/p>\n<h3 id=\"toc\">Table of Contents<\/h3>\n<ol>\n<li><a href=\"#futures\">Futures<\/a>\n<ol>\n<li><a href=\"#futures-rust\">Futures in Rust<\/a><\/li>\n<li><a href=\"#futures-async-await\">Async\/Await<\/a><\/li>\n<li><a href=\"#futures-tokio\">Tokio<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#futures-glib\">Futures &#038; GLib\/GIO<\/a>\n<ol>\n<li><a href=\"#glib-callbacks\">Callbacks<\/a><\/li>\n<li><a href=\"#glib-futures\">GLib Futures<\/a><\/li>\n<li><a href=\"#glib-gio\">GIO Asynchronous Operations<\/a><\/li>\n<li><a href=\"#glib-async-await\">Async\/Await<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#future\">The Future<\/a><\/li>\n<\/ol>\n<h3 id=\"futures\">Futures<\/h3>\n<p>First of all, what are futures and how do they work in Rust. In a few words, a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Futures_and_promises\" rel=\"noopener\" target=\"_blank\">future<\/a> (also called promise elsewhere) is a value that represents the result of an asynchronous operation, e.g. establishing a TCP connection. The operation itself (usually) runs in the background, and only once the operation is finished (or fails), the future resolves to the result of that operation. There are all kinds of ways to combine futures, e.g. to execute some other (potentially async) code with the result once the first operation has finished.<\/p>\n<p>It&#8217;s a concept that is also widely used in various other programming languages (e.g. C#, JavaScript, Python, &#8230;) for asynchronous programming and can probably be considered a proven concept at this point.<\/p>\n<h4 id=\"futures-rust\">Futures in Rust<\/h4>\n<p>In Rust, a <a href=\"https:\/\/github.com\/rust-lang-nursery\/futures-rs\" rel=\"noopener\" target=\"_blank\">future<\/a> is basically an implementation of relatively simple trait called <a href=\"https:\/\/github.com\/rust-lang-nursery\/futures-rs\/blob\/f484ffffaa39c6a8b5953825d45c9956048bfa23\/futures-core\/src\/future\/mod.rs#L47-L123\" rel=\"noopener\" target=\"_blank\">Future<\/a>. The following is the definition as of now, but there are <a href=\"https:\/\/github.com\/rust-lang\/rfcs\/pull\/2395\" rel=\"noopener\" target=\"_blank\">discussions<\/a> to change\/simplify\/generalize it currently and to also move it to the Rust standard library:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">pub trait Future {\r\n    type Item;\r\n    type Error;\r\n\r\n    fn poll(&amp;mut self, cx: &amp;mut task::Context) -&gt; Poll&lt;Self::Item, Self::Error&gt;;\r\n}<\/pre>\n<p>Anything that implements this trait can be considered an asynchronous operation that resolves to either an <i>Item<\/i> or an <i>Error<\/i>. Consumers of the future would call the <i>poll<\/i> method to check if the future has resolved already (to a result or error), or if the future is not ready yet. In case of the latter, the future itself would at a later point, once it is ready to proceed, notify the consumer about that. It would get a way for notifications from the <i>Context<\/i> that is passed, and proceeding does not necessarily mean that the future will resolve after this but it could just advance its internal state closer to the final resolution.<\/p>\n<p>Calling <i>poll<\/i> manually is kind of inconvenient, so generally this is handled by an <i>Executor<\/i> on which the futures are scheduled and which is running them until their resolution. Equally, it&#8217;s inconvenient to have to implement that trait directly so for most common operations there are combinators that can be used on futures to build new futures, usually via closures in one way or another. For example the following would run the passed closure with the successful result of the future, and then have it return another future (<i>Ok(())<\/i> is converted via <i>IntoFuture<\/i> to the future that always resolves successfully with <i>()<\/i>), and also maps any errors to <i>()<\/i><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">fn our_future() -&gt; impl Future&lt;Item = (), Err = ()&gt; {\r\n    some_future\r\n        .and_then(|res| {\r\n            do_something(res);\r\n            Ok(())\r\n        })\r\n        .map_err(|_| ())\r\n}<\/pre>\n<p>A future represents only a single value, but there is also a trait for something producing multiple values: a <i>Stream<\/i>. For more details, best to check the <a href=\"https:\/\/docs.rs\/futures\/0.2.1\/futures\/\" rel=\"noopener\" target=\"_blank\">documentation<\/a>.<\/p>\n<h4 id=\"futures-async-await\">Async\/Await<\/h4>\n<p>The above way of combining futures via combinators and closures is still not too great, and is still close to callback hell. In other languages (e.g. C#, <a href=\"https:\/\/javascript.info\/async-await\" rel=\"noopener\" target=\"_blank\">JavaScript<\/a>, Python, &#8230;) this was solved by introducing new features to the language: <i>async<\/i> for declaring futures with normal code flow, and <i>await<\/i> for suspending execution transparently and resuming at that point in the code with the result of a future.<\/p>\n<p>Of course this was also <a href=\"https:\/\/github.com\/rust-lang-nursery\/futures-rs\/blob\/master\/futures-await.md\" rel=\"noopener\" target=\"_blank\">implemented<\/a> in Rust. Currently based on procedural macros, but there are <a href=\"https:\/\/github.com\/rust-lang\/rfcs\/pull\/2394\" rel=\"noopener\" target=\"_blank\">discussions<\/a> to actually move this also directly into the language and standard library.<\/p>\n<p>The above example would look something like the following with the current version of the macros<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">#[async]\r\nfn our_future() -&gt; Result&lt;(), ()&gt; {\r\n    let res = await!(some_future)\r\n        .map_err(|_| ())?;\r\n\r\n    do_something(res);\r\n    Ok(())\r\n}<\/pre>\n<p>This looks almost like normal, synchronous code but is internally converted into a future and completely asynchronous.<\/p>\n<p>Unfortunately this is currently only available on the nightly version of Rust until various bits and pieces get stabilized.<\/p>\n<h4 id=\"futures-tokio\">Tokio<\/h4>\n<p>Most of the time when people talk about futures in Rust, they implicitly also mean <a href=\"https:\/\/tokio.rs\/\" rel=\"noopener\" target=\"_blank\">Tokio<\/a>. Tokio is a pure Rust, cross-platform asynchronous IO library and based on the futures abstraction above. It provides a futures executor and various types for asynchronous IO, e.g. sockets and socket streams.<\/p>\n<p>But while Tokio is a great library, we&#8217;re not going to use it here and instead implement a futures executor around <a href=\"https:\/\/developer.gnome.org\/glib\/stable\/\" rel=\"noopener\" target=\"_blank\">GLib<\/a>. And on top of that implement various futures, also around GLib&#8217;s sister library <a href=\"https:\/\/developer.gnome.org\/gio\/stable\/\" rel=\"noopener\" target=\"_blank\">GIO<\/a>, which is providing lots of API for synchronous and asynchronous IO.<\/p>\n<p>Just like all IO operations in Tokio, all GLib\/GIO asynchronous operations are dependent on running with their respective event loop (i.e. the futures executor) and while it&#8217;s possible to use both in the same process, each operation has to be scheduled on the correct one.<\/p>\n<h3 id=\"futures-glib\">Futures &#038; GLib\/GIO<\/h3>\n<p>Asynchronous operations and generally everything event related (timeouts, &#8230;) are based on callbacks that you have to register, and are running via a <i><a href=\"https:\/\/developer.gnome.org\/glib\/stable\/glib-The-Main-Event-Loop.html\" rel=\"noopener\" target=\"_blank\">GMainLoop<\/a><\/i> that is executing events from a <i>GMainContext<\/i>. The latter is just something that stores everything that is scheduled and provides API for polling if something is ready to be executed now, while the former does exactly that: executing.<\/p>\n<h4 id=\"glib-callbacks\">Callbacks<\/h4>\n<p>The callback based API is also available via the Rust bindings, and would for example look as follows<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">glib::timeout_add(20, || {\r\n    do_something_after_20ms();\r\n    glib::Continue(false) \/\/ don&#039;t call again\r\n});\r\n\r\nglib::idle_add(|| {\r\n    do_something_from_the_main_loop();\r\n    glib::Continue(false) \/\/ don&#039;t call again\r\n});\r\n\r\nsome_async_operation(|res| {\r\n    match res {\r\n        Err(err) =&gt; report_error_somehow(),\r\n        Ok(res) =&gt; {\r\n            do_something_with_result(res);\r\n            some_other_async_operation(|res| {\r\n                do_something_with_other_result(res);\r\n            });\r\n        }\r\n    }\r\n});<\/pre>\n<p>As can be seen here already, the callback-based approach leads to quite non-linear code and deep indentation due to all the closures. Also error handling becomes quite tricky due to somehow having handle them from a completely different call stack.<\/p>\n<p>Compared to C this is still far more convenient due to actually having closures that can capture their environment, but we can definitely do better in Rust.<\/p>\n<p>The above code also assumes that somewhere a main loop is running on the default main context, which could be achieved with the following e.g. inside <i>main()<\/i><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">let ctx = glib::MainContext::default();\r\nlet l = glib::MainLoop::new(Some(&amp;ctx), false);\r\nctx.push_thread_default();\r\n\r\n\/\/ All operations here would be scheduled on this main context\r\ndo_things(&amp;l);\r\n\r\n\/\/ Run everything until someone calls l.quit()\r\nl.run();\r\nctx.pop_thread_default();<\/pre>\n<p>It is also possible to explicitly select for various operations on which main context they should run, but that&#8217;s just a minor detail.<\/p>\n<h4 id=\"glib-futures\">GLib Futures<\/h4>\n<p>To make this situation a bit nicer, I&#8217;ve implemented support for futures in the Rust bindings. This means, that the GLib <i>MainContext<\/i> is now a futures executor (and arbitrary futures can be scheduled on it), all the <i><a href=\"https:\/\/developer.gnome.org\/glib\/stable\/glib-The-Main-Event-Loop.html#GSource\" rel=\"noopener\" target=\"_blank\">GSource<\/a><\/i> related operations in GLib (timeouts, UNIX signals, &#8230;) have futures- or stream-based variants and all the GIO asynchronous operations also come with futures variants now. The latter are autogenerated with the <a href=\"https:\/\/github.com\/gtk-rs\/gir\/\" rel=\"noopener\" target=\"_blank\">gir<\/a> bindings code generator.<\/p>\n<p>For enabling usage of this, the <i>futures<\/i> feature of the <i><a href=\"https:\/\/crates.io\/crates\/glib\" rel=\"noopener\" target=\"_blank\">glib<\/a><\/i> and <i><a href=\"https:\/\/crates.io\/crates\/gio\" rel=\"noopener\" target=\"_blank\">gio<\/a><\/i> crates have to be enabled, but that&#8217;s about it. It is currently still hidden behind a feature gate because the futures infrastructure is still going to go through some API incompatible changes in the near future.<\/p>\n<p>So let&#8217;s take a look at how to use it. First of all, setting up the main context and executing a trivial future on it<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">let c = glib::MainContext::default();\r\nlet l = glib::MainLoop::new(Some(&amp;c), false);\r\n\r\nc.push_thread_default();\r\n\r\n\/\/ Spawn a future that is called from the main context\r\n\/\/ and after printing something just quits the main loop\r\nlet l_clone = l.clone();\r\nc.spawn(futures::lazy(move |_| {\r\n    println!(&quot;we&#039;re called from the main context&quot;);\r\n    l_clone.quit();\r\n    Ok(())\r\n});\r\n\r\nl.run();\r\n\r\nc.pop_thread_default();<\/pre>\n<p>Apart from <i>spawn()<\/i>, there is also a <i>spawn_local()<\/i>. The former can be called from any thread but requires the future to implement the <i>Send<\/i> trait (that is, it must be safe to send it to other threads) while the latter can only be called from the thread that owns the main context but it allows any kind of future to be spawned. In addition there is also a <i>block_on()<\/i> function on the main context, which allows to run non-static futures up to their completion and returns their result. The spawn functions only work with static futures (i.e. they have no references to any stack frame) and requires the futures to be infallible and resolve to <i>()<\/i>.<\/p>\n<p>The above code already showed one of the advantages of using futures: it is possible to use all generic futures (that don&#8217;t require a specific executor), like <i><a href=\"https:\/\/docs.rs\/futures\/0.2.1\/futures\/future\/fn.lazy.html\" rel=\"noopener\" target=\"_blank\">futures::lazy<\/a><\/i> or the <a href=\"https:\/\/docs.rs\/futures\/0.2.1\/futures\/channel\/index.html\" rel=\"noopener\" target=\"_blank\">mpsc\/oneshot channels<\/a> with GLib now. And any of the combinators that are available on futures<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">let c = MainContext::new();\r\n                                                                                                       \r\nlet res = c.block_on(timeout_future(20)\r\n    .and_then(move |_| {\r\n        \/\/ Called after 20ms\r\n        Ok(1)\r\n    })\r\n);\r\n\r\nassert_eq!(res, Ok(1));<\/pre>\n<p>This example also shows the <i>block_on<\/i> functionality to return an actual value from the future (1 in this case).<\/p>\n<h4 id=\"glib-gio\">GIO Asynchronous Operations<\/h4>\n<p>Similarly, all asynchronous GIO operations are now available as futures. For example to open a file asynchronously and getting a <i>gio::InputStream<\/i> to read from, the following could be done<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">let file = gio::File::new_for_path(&quot;Cargo.toml&quot;);\r\n\r\nlet l_clone = l.clone();\r\nc.spawn_local(\r\n    \/\/ Try to open the file\r\n    file.read_async_future(glib::PRIORITY_DEFAULT)\r\n        .map_err(|(_file, err)| {\r\n            format!(&quot;Failed to open file: {}&quot;, err)\r\n        })\r\n        .and_then(move |(_file, strm)| {\r\n            \/\/ Here we could now read from the stream, but\r\n            \/\/ instead we just quit the main loop\r\n            l_clone.quit();\r\n\r\n            Ok(())\r\n        })\r\n);<\/pre>\n<p>A bigger example can be found in the gtk-rs examples repository <a href=\"https:\/\/github.com\/gtk-rs\/examples\/blob\/a684d90e4233d6eeab8aff5e65013cd9208b409f\/src\/bin\/gio_futures.rs\" rel=\"noopener\" target=\"_blank\">here<\/a>. This example is basically reading a file asynchronously in 64 byte chunks and printing it to stdout, then closing the file.<\/p>\n<p>In the same way, network operations or any other asynchronous operation can be handled via futures now.<\/p>\n<h4 id=\"glib-async-await\">Async\/Await<\/h4>\n<p>Compared to a callback-based approach, that bigger example is already a lot nicer but still quite heavy to read. With the async\/await extension that I mentioned above already, the code looks much nicer in comparison and really almost like synchronous code. Except that it is not synchronous.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-title=\"\">#[async]\r\nfn read_file(file: gio::File) -&gt; Result&lt;(), String&gt; {\r\n    \/\/ Try to open the file\r\n    let (_file, strm) = await!(file.read_async_future(glib::PRIORITY_DEFAULT))\r\n        .map_err(|(_file, err)| format!(&quot;Failed to open file: {}&quot;, err))?;\r\n\r\n    Ok(())\r\n}\r\n\r\nfn main() {\r\n    [...]\r\n    let future = async_block! {\r\n        match await!(read_file(file)) {\r\n            Ok(()) =&gt; (),\r\n            Err(err) =&gt; eprintln!(&quot;Got error: {}&quot;, err),\r\n        }\r\n        l_clone.quit();\r\n        Ok(())\r\n    };\r\n\r\n    c.spawn_local(future);\r\n    [...]\r\n}<\/pre>\n<p>For compiling this code, the <i>futures-nightly<\/i> feature has to be enabled for the <i>glib<\/i> crate, and a nightly compiler must be used.<\/p>\n<p>The bigger example from before with async\/await can be found <a href=\"https:\/\/github.com\/gtk-rs\/examples\/blob\/a684d90e4233d6eeab8aff5e65013cd9208b409f\/src\/bin\/gio_futures_await.rs\" rel=\"noopener\" target=\"_blank\">here<\/a>.<\/p>\n<p>With this we&#8217;re already very close in Rust to having the same convenience as in other languages with asynchronous programming. And also it is very similar to what is possible in <a href=\"https:\/\/wiki.gnome.org\/Projects\/Vala\/AsyncSamples\" rel=\"noopener\" target=\"_blank\">Vala<\/a> with GIO asynchronous operations.<\/p>\n<h3 id=\"future\">The Future<\/h3>\n<p>For now this is all finished and available from GIT of the <i>glib<\/i> and <i>gio<\/i> crates. This will have to be updated in the future whenever the futures API is changing, but it is planned to stabilize all this in Rust until the end of this year.<\/p>\n<p>In the future it might also make sense to add futures variants for all the GObject signal handlers, so that e.g. handling a click on a GTK+ button could be done similarly from a future (or rather from a <i>Stream<\/i> as a signal can be emitted multiple times). If this is in the end more convenient than the callback-based approach that is currently used, is to be seen. Some experimentation would be necessary here. Also how to handle return values of signal handlers would have to be figured out.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Unfortunately I was not able to attend the Rust+GNOME hackfest in Madrid last week, but I could at least spend some of my work time at Centricular on implementing one of the things I wanted to work on during the hackfest. The other one, more closely related to the gnome-class work, will be the topic &hellip; <a href=\"https:\/\/coaxion.net\/blog\/2018\/04\/glib-gio-async-operations-and-rust-futures-async-await\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">GLib\/GIO async operations and Rust futures + async\/await<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[3,6,53],"tags":[],"class_list":["post-627","post","type-post","status-publish","format-standard","hentry","category-free-software","category-gnome","category-rust"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts\/627","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/comments?post=627"}],"version-history":[{"count":12,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts\/627\/revisions"}],"predecessor-version":[{"id":639,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts\/627\/revisions\/639"}],"wp:attachment":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/media?parent=627"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/categories?post=627"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/tags?post=627"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}