<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bernard Kolobara</title>
    <description>The latest articles on DEV Community by Bernard Kolobara (@bkolobara).</description>
    <link>https://dev.to/bkolobara</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F433253%2F2ce4c8d7-fa8c-4010-bba2-506d7db629a9.jpg</url>
      <title>DEV Community: Bernard Kolobara</title>
      <link>https://dev.to/bkolobara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bkolobara"/>
    <language>en</language>
    <item>
      <title>Building a telnet chat server with Rust and Lunatic</title>
      <dc:creator>Bernard Kolobara</dc:creator>
      <pubDate>Mon, 22 Feb 2021 14:44:20 +0000</pubDate>
      <link>https://dev.to/bkolobara/how-i-built-a-telnet-chat-server-in-2021-with-webassembly-2711</link>
      <guid>https://dev.to/bkolobara/how-i-built-a-telnet-chat-server-in-2021-with-webassembly-2711</guid>
      <description>&lt;p&gt;I love the aesthetics of terminals and I’m not the only one, there is a whole &lt;a href="https://www.reddit.com/r/unixporn/" rel="noopener noreferrer"&gt;subreddit&lt;/a&gt; dedicated to people sharing their desktops and showcasing different terminal setups. Last year I spent working on &lt;a href="https://github.com/lunatic-solutions/lunatic" rel="noopener noreferrer"&gt;an innovative WebAssembly runtime called Lunatic&lt;/a&gt;. Recently we landed TCP support and I was super excited to start building real world applications with it, and what would be a better fit than a terminal based chat server with a &lt;a href="https://thenewstack.io/the-lost-worlds-of-telnet/" rel="noopener noreferrer"&gt;retro vibe&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;It took me around a week to build it with Rust + &lt;a href="https://github.com/lunatic-solutions/lunatic" rel="noopener noreferrer"&gt;Lunatic&lt;/a&gt; and you can check out the code &lt;a href="https://github.com/lunatic-solutions/chat" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you would like to try it out you can connect to it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# US&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; telnet lunatic.chat
&lt;span class="c"&gt;# EU&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; telnet eu.lunatic.chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While writing the server I ran into many interesting problems and would like to share here how I leveraged the power of Rust and Lunatic to overcome them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The reason I picked telnet is that the &lt;a href="https://tools.ietf.org/html/rfc854" rel="noopener noreferrer"&gt;specification&lt;/a&gt; is simple enough to read through and implement in a short time. It’s a small layer on top of TCP and as mentioned before we had TCP already working. On the other hand, telnet is a really limiting protocol and I needed to get creative while building a chat application on top of it.&lt;/p&gt;

&lt;p&gt;The first issue I encountered was the line based nature of terminals. You write a command, hit enter and the terminal prints out some text. This doesn’t go well with the UI of a chat app where&lt;br&gt;
messages can come in at any time. What are you supposed to do when new text arrives and the user has already partially written her own message? Override the user's input? Print the new message after the input?&lt;/p&gt;

&lt;p&gt;One solution would be to buffer all messages until the user hits enter and then just dump all the ones that arrived in the meantime at once, but this can’t work as we would rely on the user to keep hitting enter to read new messages.&lt;/p&gt;

&lt;p&gt;It became clear that I needed to use some kind of terminal user interface where I render separately all the incoming messages from the user who is currently typing. It’s possible to do this by using &lt;a href="https://tools.ietf.org/html/rfc1073" rel="noopener noreferrer"&gt;a few&lt;/a&gt; &lt;a href="https://tools.ietf.org/html/rfc1184" rel="noopener noreferrer"&gt;extensions&lt;/a&gt; to the telnet protocol. Once the telnet client connects I send it the following instructions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don’t echo anything that the user is typing, let me be in charge of printing in the terminal.&lt;/li&gt;
&lt;li&gt;Don’t buffer messages, send each keystroke to the server.&lt;/li&gt;
&lt;li&gt;Report size changes of the terminal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This allows me to construct the UI on the server and just send a sequence of terminal escape characters back to bring the user’s terminal up to date. On each keystroke or message received the UI is updated.&lt;/p&gt;
&lt;h3&gt;
  
  
  Massive concurrency
&lt;/h3&gt;

&lt;p&gt;For this to work we need to permanently keep the telnet connection open and periodically send data through it. This is a perfect use case for Lunatic’s Processes, they are designed for massive concurrency. Each client’s connection is handled in a separate Process.&lt;/p&gt;

&lt;p&gt;Not to be confused with Operating System processes, Lunatic’s Processes are lightweight and also known as green threads (but isolated) or &lt;a href="https://golangbot.com/goroutines" rel="noopener noreferrer"&gt;go-routines&lt;/a&gt; in other runtimes. They are fast to create, have a small memory footprint and a low scheduling overhead. All Processes are preemptively scheduled and they can’t spend too much time running without yielding and giving others a fair share of the resources. This keeps all connections responsive in an environment where most of the time is spent waiting on I/O.&lt;/p&gt;
&lt;h3&gt;
  
  
  Interop with existing libraries
&lt;/h3&gt;

&lt;p&gt;Luckily I could make use of existing Rust libraries and didn’t need to reinvent the wheel. I used:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/djc/askama" rel="noopener noreferrer"&gt;Askma&lt;/a&gt; as a templating engine.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fdehau/tui-rs" rel="noopener noreferrer"&gt;TUI&lt;/a&gt; as the rendering engine.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/chronotope/chrono" rel="noopener noreferrer"&gt;Chrono&lt;/a&gt; for date formatting.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;They all compiled to WebAssembly without issues. I just needed to provide a telnet backend for TUI, but I could reuse most of the code from the &lt;a href="https://github.com/redox-os/termion" rel="noopener noreferrer"&gt;termion&lt;/a&gt; crate (sadly it has no Windows support for now).&lt;/p&gt;

&lt;p&gt;TUI works in a somewhat similar way to React.js, you update your state and just call a render method. It will re-render the UI and send back to the client the minimal amount of changes in the form of terminal escape characters.&lt;/p&gt;
&lt;h3&gt;
  
  
  State Management
&lt;/h3&gt;

&lt;p&gt;A big part of programming is state management. Your application getting into a state that you couldn’t predict while writing the code is a big source of bugs, and Lunatic tries to simplify this by allowing you to isolate the state into separate processes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixqlkl8op5de0i7c06hz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixqlkl8op5de0i7c06hz.png" alt="Processes visualisation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the perspective of a process, it owns the whole memory and can’t influence the memory of other processes in any way, not even by unsafe pointer dereferencing. This is a result of building&lt;br&gt;
them on top of WebAssembly instances. The only way processes can talk between each other is through message passing.&lt;/p&gt;

&lt;p&gt;This greatly simplifies reasoning about state changes. You only need to think about what state you are in and how the next message will influence the state change. It makes it a lot easier to debug once you find yourself in an undesirable state. Let’s look at a concrete code sample from the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The server is the main coordinator between all the clients.&lt;/span&gt;
&lt;span class="c1"&gt;// It keeps track of connected clients and active channels.&lt;/span&gt;
&lt;span class="c1"&gt;// It's also in charge of assigning unique usernames to new clients.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;server_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_receiver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Receiver&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServerMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ServerState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;username_generator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;all_usernames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HashSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;state_receiver&lt;/span&gt;&lt;span class="nf"&gt;.receive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;ServerMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Joined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Increase the number of active users&lt;/span&gt;
                &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="py"&gt;.clients&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="c1"&gt;// Generate a new username&lt;/span&gt;
                &lt;span class="n"&gt;username_generator&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User_{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                   &lt;span class="n"&gt;username_generator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;all_usernames&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="c1"&gt;// Client specific state&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;server_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ServerInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="py"&gt;.clients&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;ServerMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="err"&gt;….&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;ServerMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ChangeName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;all_usernames&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// Notify client that the name is taken&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;all_usernames&lt;/span&gt;&lt;span class="nf"&gt;.remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;all_usernames&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that the process has a few local variables to keep track of its state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;How many clients are connected.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Which channels are available.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;If a new user joins what username should be assigned.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Which usernames are taken.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Afterwards the Process just runs in a loop waiting on messages. If a new client is connected the server receives a &lt;code&gt;ServerMessage::Joined&lt;/code&gt; message. It will then update the total count of users, assign a new username to the client and send back a message notifying the client about the assigned username.&lt;/p&gt;

&lt;p&gt;The client’s process is similarly structured, it keeps a state of the current input box for each channel and all received messages for the channels. The client’s process can receive 2 types of messages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Keystrokes coming from the telnet connection.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;New chat messages coming from all subscribed channels.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each keystroke we update the current channel’s input box or attach the new message to the history of messages in the channel.&lt;/p&gt;

&lt;p&gt;If we have such an architecture and run into a bug, let’s say the number of connected users shown is wrong, there is only one source of truth here and we know exactly where this information came from. We just need to figure out how we got into this state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other benefits
&lt;/h3&gt;

&lt;p&gt;There are some not so obvious additional benefits that we get from Lunatic.&lt;/p&gt;

&lt;p&gt;If a client’s process receives some malicious data from the telnet connection and crashes, it will only terminate the existing connection. It can’t access the state of any other Processes. In my first implementation I was often using &lt;code&gt;.unwrap&lt;/code&gt; in the code, following &lt;a href="https://verraes.net/2014/12/erlang-let-it-crash/" rel="noopener noreferrer"&gt;Erlang’s let it crash philosophy&lt;/a&gt; and knowing that if I see any crashes in the logs I can always later investigate why they happened, but the application should continue running.&lt;/p&gt;

&lt;p&gt;The message sending implementation uses &lt;a href="https://docs.rs/smol/1.2.5/smol/channel/index.html" rel="noopener noreferrer"&gt;Smol’s channels&lt;/a&gt; underneath, but you may be surprised not to see any &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;.await&lt;/code&gt; keywords in the code. The reason for this is that Lunatic abstracts away the asynchronous code and you can just write seemingly blocking code, but it actually never blocks the underlying thread and takes full advantage of async Rust. This is a whole topic on its own so I will leave it for another blog post.&lt;/p&gt;

&lt;p&gt;Lunatic works with any code that can compile to WebAssembly, and as I have shown earlier a lot of libraries just work out of the box. You can also &lt;a href="https://users.rust-lang.org/t/how-to-static-link-c-lib-to-wasm/36558/5" rel="noopener noreferrer"&gt;link C code into your Rust application&lt;/a&gt; while compiling to WebAssembly. One big pain point when using C from Erlang is that you need to be extremely careful in your code, because if something crashes it will take the whole VM down. Or if you spend too much time in the C part it will block the scheduler from using the thread and endanger the responsiveness of your system. Lunatic solves both of these problems. The reduction counter is inserted before the WebAssembly code is JIT translated to machine code and will also be part of the “native” C code, allowing the scheduler to preempt it. A crash still stays isolated thanks to WebAssembly sandboxing properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In the end the chat server will remain a nice toy application and you should not use it for more serious use cases as telnet doesn’t encrypt any of the data sent to the server.&lt;/p&gt;

&lt;p&gt;However, it’s a really good feeling to get something like this running on a runtime you have built. While developing the chat application I found a few bugs in the runtime itself, so it was totally worth creating this app . I’m really looking forward to gradually moving away from building Lunatic and building amazing applications with it. I was also positively surprised how well the chat app is working, being the first real word app built on Lunatic.&lt;/p&gt;

&lt;p&gt;I think that we are finally at the point where WebAssembly is mature enough to be used in serious applications, and I strongly believe that WebAssembly on the backend is going to play an important role in the future.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Writing Rust the Elixir way</title>
      <dc:creator>Bernard Kolobara</dc:creator>
      <pubDate>Sat, 28 Nov 2020 10:14:13 +0000</pubDate>
      <link>https://dev.to/bkolobara/writing-rust-the-elixir-way-2lm8</link>
      <guid>https://dev.to/bkolobara/writing-rust-the-elixir-way-2lm8</guid>
      <description>&lt;p&gt;It's not a secret that I'm a big fan of &lt;a href="https://elixir-lang.org/"&gt;Elixir&lt;/a&gt;, so when I started doing Rust development I tried to bring some ideas from Elixir to the world of &lt;a href="https://www.rust-lang.org/"&gt;Rust&lt;/a&gt;. This post describes some of the tools I'm building to bring the power of Elixir to Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes Elixir so great?
&lt;/h2&gt;

&lt;p&gt;It's hard to just pick a few of them, but I believe that Elixir's biggest advantage comes from using &lt;a href="https://www.erlang.org/"&gt;Erlang&lt;/a&gt; as the underlying virtual machine, specially from these 2 properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Massive concurrency&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fault tolerance&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Massive concurrency
&lt;/h3&gt;

&lt;p&gt;This is something hard to explain until you experience it yourself. I learned early in my career that you should &lt;strong&gt;never&lt;/strong&gt; create a thread while handling a request. Threads are heavy, expensive and too many of them can bring your whole machine down. In most cases it's enough to use a &lt;a href="https://en.wikipedia.org/wiki/Thread_pool"&gt;thread pool&lt;/a&gt;, but this approach fails once the number of concurrent tasks outgrows the number of threads in the pool.&lt;/p&gt;

&lt;p&gt;Let's look at an example: imagine a rust application that just creates 2000 threads that wake up every 100 ms and go right back to sleep.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2_000&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_millis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though the threads don't do anything, just running this on my MacBook forces it to reboot after a few seconds. This makes it impractical to have massive concurrency with threads. There are many solutions to this problem. The one chosen by Elixir is to abstract concurrent tasks with something called &lt;a href="https://elixir-lang.org/getting-started/processes.html"&gt;Processes&lt;/a&gt;. They are extremely lightweight, so even running &lt;a href="https://www.phoenixframework.org/blog/the-road-to-2-million-websocket-connections"&gt;2 million of them&lt;/a&gt; doesn't present a challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Massive concurrency in Rust
&lt;/h3&gt;

&lt;p&gt;You can achieve &lt;a href="https://github.com/actix/actix-web"&gt;amazing concurrency and performance&lt;/a&gt; using &lt;strong&gt;async Rust&lt;/strong&gt;, but working with async Rust is not as simple as writing regular Rust code and it just doesn't provide you the same features as Elixir Processes do.&lt;/p&gt;

&lt;p&gt;After thinking for a long time how I could make something that reassembles Elixir Processes in Rust I came up with the idea to introduce an intermediate step, &lt;a href="https://webassembly.org/"&gt;WebAssembly&lt;/a&gt;. WebAssembly is a low level bytecode specification that Rust can target. The idea was simple, instead of compiling Rust for x86-64 you would compile it to the WASM target. From there I would build a set of libraries and a WebAssembly runtime that exposes the concept of &lt;strong&gt;Rust Processes&lt;/strong&gt;. Contrary to operating system processes or threads, they are lightweight with small memory footprints, fast to create and terminate, and the scheduling overhead is low. In other languages they are also known as &lt;a href="https://en.wikipedia.org/wiki/Green_threads"&gt;green threads&lt;/a&gt; and &lt;a href="https://golangbot.com/goroutines/"&gt;goroutines&lt;/a&gt;, but I will call them processes to stay close to Elixir's naming convention.&lt;/p&gt;

&lt;p&gt;That was the first step towards &lt;a href="https://github.com/lunatic-lang/lunatic"&gt;Lunatic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's look at the same Rust example, but now implemented with Lunatic. At the same time we will crank up the number of concurrent processes to 20k.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;lunatic&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;20_000&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;((),&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="nf"&gt;.receive&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run this you will need to compile this Rust code to a &lt;code&gt;.wasm&lt;/code&gt; file first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;○ →  cargo build --release --target=wasm32-wasi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;○ →  lunaticvm example.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Contrary to the previous example this runs without hiccups on my Late 2013 Macbook and the CPU utilisation is minimal, even if we are using 10x more concurrent tasks. Let's examine what is exactly happening here.&lt;/p&gt;

&lt;p&gt;The processes spawned by Lunatic are actually taking full advantage of the power provided by async Rust. They are scheduled on top of a &lt;a href="https://docs.rs/smol/1.2.4/smol/struct.Executor.html"&gt;work stealing async executor&lt;/a&gt;, the same used by &lt;a href="https://github.com/async-rs/async-std"&gt;async-std&lt;/a&gt;. Calling &lt;code&gt;Process::sleep(100)&lt;/code&gt; will actually invoke &lt;a href="https://docs.rs/smol/1.2.4/smol/struct.Timer.html#method.at"&gt;smol's &lt;code&gt;at&lt;/code&gt; function&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Wait a second! How does this work without the &lt;code&gt;.await&lt;/code&gt; keyword, you may ask yourself. Lunatic takes the same approach as Go, Erlang and the earlier implementation of Rust based on green threads. It creates a tiny stack for executing the process and grows it when your applications needs more. This is a bit less efficient than calculating the exact stack size during compile time as async Rust is doing, but a reasonable tradeoff I would say.&lt;/p&gt;

&lt;p&gt;Now you can write regular &lt;strong&gt;blocking code&lt;/strong&gt;, but the executor will take care of moving your process off the execution thread if you are waiting, so you never block a thread.&lt;/p&gt;

&lt;p&gt;As we saw earlier, scheduling threads is a hard task for the operating system. To replace one thread that's being executed with another one, a lot of work needs to be done (including saving all the registers and some thread state). However, switching between Lunatic Processes does only the minimal amount of work possible. With an idea pioneered by the &lt;a href="https://github.com/edef1c/libfringe"&gt;libfringe library&lt;/a&gt; and using some &lt;a href="https://github.com/rust-lang/rust/issues/72016"&gt;asm! macro&lt;/a&gt; magic, Lunatic lets the Rust compiler figure out the minimal number of registers to be preserved during context switches. &lt;strong&gt;This makes scheduling Lunatic processes zero-cost&lt;/strong&gt;. On my machine usually 1ns, equivalent to a function call.&lt;/p&gt;

&lt;p&gt;Another benefit of scheduling the Processes in user space instead of using threads is that other applications will continue running normally on your machine, even if your app misbehaves.&lt;/p&gt;

&lt;p&gt;Now that we saw how Lunatic allows you to create applications with massive concurrency, let's look at fault tolerance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fault tolerance
&lt;/h3&gt;

&lt;p&gt;Maybe the most known Eralng/Elixir philosophy is &lt;a href="https://medium.com/@vamsimokari/erlang-let-it-crash-philosophy-53486d2a6da"&gt;"let it crash"&lt;/a&gt;. If you are building complex systems it's impossible to predict all failure scenarios. Inevitably something is going to fail in your application, but this failure should not bring down the whole thing.&lt;/p&gt;

&lt;p&gt;Elixir Processes are completely isolated and can only communicate through messages with each other. This allows you to design your application in a way that failure stays contained inside one process and doesn't affect the rest of them.&lt;/p&gt;

&lt;p&gt;Lunatic provides even stronger guarantees than Erlang here.&lt;br&gt;
&lt;strong&gt;Each Lunatic process gets their own heap, stack and syscalls.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's look at an example of a simple TCP echo server in Lunatic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;lunatic&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c"&gt;// Once WASI gets networking support you will be able to use Rust's `std::net::TcpStream` instead.&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;BufRead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:1337"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tcp_stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tcp_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tcp_stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf_reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tcp_stream&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;buf_reader&lt;/span&gt;&lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;tcp_stream&lt;/span&gt;&lt;span class="nf"&gt;.write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This application listens on &lt;code&gt;localhost:1337&lt;/code&gt; for tcp connections, spawns a process to handle each incoming connection and just echoes incoming lines.&lt;/p&gt;

&lt;p&gt;You can test it using &lt;code&gt;telnet&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;○ → telnet 127.0.0.1 1337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello world
Hello world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing you will notice is that we don't use any &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;.await&lt;/code&gt; keywords, even though this application will fully utilise Rust's async IO under the hood.&lt;/p&gt;

&lt;p&gt;Also, the tcp connection becomes fully encapsulated in the Process,&lt;br&gt;
even if we called into unsafe C code that crashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tcp_stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;crashing_c_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The crash is only contained to one connection in this case. It's not possible to implement something like this in Elixir, because if a call to a C function crashes it will take the whole virtual machine with it.&lt;/p&gt;

&lt;p&gt;Another feature exclusive to Lunatic is the possibility to limit processes' syscall access. If we replaced the previous &lt;code&gt;spawn&lt;/code&gt; call with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// Process::spawn_without_fs is not implemented yet.&lt;/span&gt;
&lt;span class="nn"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn_without_fs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tcp_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;any code called from inside the &lt;code&gt;handle&lt;/code&gt; function would be forbidden from using syscalls for filesystem access. This works also for C dependencies, because the enforcement is happening at such a low level. It allows you to express the sandboxing requirements of a Process and to use any dependency without fear. I'm not aware of any other runtime that allows you to do this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The future
&lt;/h3&gt;

&lt;p&gt;This is just a teaser of the capabilities that &lt;a href="https://github.com/lunatic-lang/lunatic"&gt;Lunatic&lt;/a&gt; will provide. There are many more features coming. Once you have this foundation, a new world of possibilities opens up. Some of the features I'm excited about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The ability to transparently move Processes from one machine to another. The programming model relies on processes communicating through messages and if these messages are sent locally or between different computers on a network it doesn't really matter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hot reloading. Now that we have the WASM bytecode as an in-between step it becomes possible to just generate new JIT machine code from it and replace it while the whole system is still running.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Running complete applications compiled to WASM as a process. One example would be redirecting file read/writes from the application to tcp streams, as we are in complete charge of syscalls. The advantage here is that you are modelling the execution environment with code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lunatic is still in its early days, so there is a lot of development left to do. If you are excited about it or have some ideas you would like to use Lunatic for, reach out to me over email &lt;a href="//mailto:me@kolobara.com"&gt;me@kolobara.com&lt;/a&gt; or on twitter &lt;a href="https://twitter.com/bkolobara"&gt;@bkolobara&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also want to use this opportunity to say a big thank you to the teams working on Rust, &lt;a href="https://wasmer.io/"&gt;Wasmer&lt;/a&gt;, &lt;a href="https://wasmtime.dev/"&gt;Wasmtime&lt;/a&gt;, &lt;a href="https://github.com/bytecodealliance/lucet"&gt;Lucet&lt;/a&gt; and &lt;a href="https://wascc.dev/"&gt;waSCC&lt;/a&gt;. It would be impossible to build Lunatic without all the hard work put into this projects.&lt;/p&gt;

&lt;p&gt;P.S. If you would like to learn more about the magic of Erlang and Elixir, this is one of my favorite talks about it by &lt;a href="https://www.youtube.com/watch?v=JvBT4XBdoUE"&gt;Saša Jurić: The Soul of Erlang and Elixir&lt;/a&gt;. Seriously, go and watch it!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Lunatic: Actor based WebAssembly runtime for the backend</title>
      <dc:creator>Bernard Kolobara</dc:creator>
      <pubDate>Thu, 27 Aug 2020 16:33:57 +0000</pubDate>
      <link>https://dev.to/bkolobara/lunatic-actor-based-webassembly-runtime-for-the-backend-36oj</link>
      <guid>https://dev.to/bkolobara/lunatic-actor-based-webassembly-runtime-for-the-backend-36oj</guid>
      <description>&lt;p&gt;I would like to share with you a project I have been working on for the last few months. &lt;strong&gt;Lunatic&lt;/strong&gt; is a WASM runtime heavily inspired by Erlang and Go. I tried to take all the cool parts of these VMs and extend them with a few new features that are interesting to me. Mostly I focused on having a great VM for low latency networking applications. It's still not public, so you will need to be a bit patient before trying it out.&lt;/p&gt;

&lt;p&gt;The main features are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Actor_model"&gt;Actors&lt;/a&gt; are represented as WebAssembly instances.&lt;/li&gt;
&lt;li&gt;Sandboxing and runtime permissions are on a per actor basis.&lt;/li&gt;
&lt;li&gt;All blocking code is transformed into asynchronous automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's written in Rust using &lt;a href="https://wasmtime.dev/"&gt;Wasmtime&lt;/a&gt; and &lt;a href="https://tokio.rs/"&gt;Tokio&lt;/a&gt;, plus a custom stack switching implementation inspired by &lt;a href="https://github.com/edef1c/libfringe"&gt;libfringe&lt;/a&gt; and an async stdlib implementation compatible with &lt;a href="https://wasi.dev/"&gt;WASI&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How does everything fit together?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The architecture of Lunatic is quite simple. Tokio is used to schedule lightweight tasks (actors) consisting of WASM instances. Each instance is self contained (has its own stack and heap) and sandboxed, but still lightweight enough so I can &lt;em&gt;spawn 300k/s tasks&lt;/em&gt; on my almost decade old MacBook.&lt;/p&gt;

&lt;p&gt;In this case you can just have your web app spawn a new WASM instance for each request. Opposite of similar runtimes (Go/Erlang) you have per actor precise permissions. You can only allow files being opened from a specific folder, forbid outgoing network requests or limit memory usage. You can also use safely any existing C library inside the actor, knowing that any vulnerability or crash will just be limited to this particular request.&lt;/p&gt;

&lt;p&gt;From the application's (that runs on top of Lunatic) perspective, all operations are blocking. You write your regular old non-async rust or C++ code, but once you compile it to WASM and run on top of Lunatic, all the blocking calls are transformed into async alternatives. Your code is simplified because you don't need to use async notations and can mix in existing C libraries with blocking syscalls, but the runtime will take care of using async alternatives instead and suspending the actors.&lt;/p&gt;

&lt;p&gt;Having &lt;a href="https://www.phoenixframework.org/blog/the-road-to-2-million-websocket-connections"&gt;2 million connections&lt;/a&gt; in your Rust application, but not needing to reach out to async code should be a nice experience :).&lt;/p&gt;

&lt;p&gt;Using WASM as the bytecode allows Lunatic to be targeted by any language that compiles to WASM, including Rust, C/C++, Go, AssemblyScript and others. It is also possible to write different actors in different languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Low latency&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One issue that is common across async Rust, Erlang and Go is that to assure low latency the runtime can't spend too much time on a single task. You need to yield back from this particular task for the system to re-schedule it. Or it will end up blocking a thread for a long time. This is easy if you stay inside the particular language, but really hard if you call out into C code. The advantage of Lunatic here is that all code is compiled to WASM, even C extensions and it will always periodically yield back, even if you have a tight infinite loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Future development&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Originally I started working on a programming language, but realised I could not accomplish the things I wanted with existing VMs. So I ended up developing another runtime.&lt;/p&gt;

&lt;p&gt;It's still early in development and I'm constantly experimenting with different features before settling down for a final design. Lately I have been playing with implementing the threading part of the standard library so you could just spawn new threads from your existing language but they will actually be abstracted as actors in Lunatic.&lt;/p&gt;

&lt;p&gt;If you want to follow along with the development, &lt;a href="https://twitter.com/bkolobara"&gt;I intend to tweet now more frequently about it&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
