<?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>Forem: Sumant</title>
    <description>The latest articles on Forem by Sumant (@sumant1122).</description>
    <link>https://forem.com/sumant1122</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%2F447808%2F139fa8ad-ce82-47f1-ab9e-18262146985d.jpeg</url>
      <title>Forem: Sumant</title>
      <link>https://forem.com/sumant1122</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sumant1122"/>
    <language>en</language>
    <item>
      <title>Building an Async Rust Runtime on io_uring: 7.5ms vs Tokio's 14.9ms</title>
      <dc:creator>Sumant</dc:creator>
      <pubDate>Tue, 05 May 2026 07:30:04 +0000</pubDate>
      <link>https://forem.com/sumant1122/building-an-async-rust-runtime-on-iouring-75ms-vs-tokios-149ms-2f64</link>
      <guid>https://forem.com/sumant1122/building-an-async-rust-runtime-on-iouring-75ms-vs-tokios-149ms-2f64</guid>
      <description>&lt;p&gt;You use &lt;code&gt;async/await&lt;/code&gt; every day. But do you know what actually happens when your code "pauses"? I didn't, so I built something to find out.&lt;/p&gt;

&lt;p&gt;The result is &lt;strong&gt;&lt;a href="https://github.com/sumant1122/ringcore" rel="noopener noreferrer"&gt;RingCore&lt;/a&gt;&lt;/strong&gt;, a minimal async runtime in Rust, built directly on Linux's &lt;code&gt;io_uring&lt;/code&gt;, with zero abstraction layers in the way. No Tokio. No hidden thread pools. Just Rust, a kernel interface, and a lot of curiosity.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Question That Started Everything
&lt;/h2&gt;

&lt;p&gt;If you've written async Rust, you've probably typed this:&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;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it just works. The program doesn't freeze. Other tasks keep running.&lt;/p&gt;

&lt;p&gt;But I kept asking: &lt;strong&gt;what is actually happening when &lt;code&gt;.await&lt;/code&gt; suspends a task?&lt;/strong&gt; Where does execution go? Who wakes it back up? How does the OS fit into any of this?&lt;/p&gt;

&lt;p&gt;Most tutorials stop at "the runtime handles it." That answer never satisfied me.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Does Async Exist at All?
&lt;/h2&gt;

&lt;p&gt;Imagine you're a chef in a kitchen. You put a steak on the grill and just stand there watching it cook. You don't prep the salad. You don't plate the dessert. You just wait.&lt;/p&gt;

&lt;p&gt;That's &lt;strong&gt;synchronous I/O&lt;/strong&gt;. Your program calls &lt;code&gt;read()&lt;/code&gt;, the OS fetches data from disk or the network, and your thread sits idle until it comes back. Wasteful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Async I/O&lt;/strong&gt; lets you be a smarter chef. You start the steak, set a timer, and go do other things. When the timer fires, you come back and finish.&lt;/p&gt;

&lt;p&gt;In Rust, &lt;code&gt;async/await&lt;/code&gt; is the language-level mechanism for writing this kind of code. But Rust itself doesn't define &lt;em&gt;how&lt;/em&gt; the waiting works, that's the runtime's job. Most people reach for &lt;strong&gt;Tokio&lt;/strong&gt;, which is fantastic and production-ready. But it's also a black box.&lt;/p&gt;

&lt;p&gt;I wanted the white box.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter io_uring: The Kernel's Secret Weapon
&lt;/h2&gt;

&lt;p&gt;Traditional async I/O on Linux is expensive. Every interaction with the kernel requires a &lt;strong&gt;context switch&lt;/strong&gt;, which is a CPU jump from user mode (your program) into kernel mode (the OS) and back. Under heavy I/O load, these add up fast.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;io_uring&lt;/code&gt;, introduced in Linux 5.1 by kernel developer Jens Axboe, takes a radically different approach. Instead of making individual system calls, your program and the kernel share two ring buffers in memory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Submission Queue (SQ):&lt;/strong&gt; You write your I/O requests here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Completion Queue (CQ):&lt;/strong&gt; The kernel writes results back here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like a diner counter with a ticket window. Instead of running to the kitchen for every order, you slide all your tickets through the window at once and the kitchen slides the finished plates back. One trip. Maximum efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple I/O operations can be batched into a single &lt;code&gt;io_uring_enter&lt;/code&gt; system call.&lt;/strong&gt; Context switches plummet. Performance soars.&lt;/p&gt;




&lt;h2&gt;
  
  
  How RingCore Works: A Tour of the Four Layers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Layer 1: Talking to the Kernel (&lt;code&gt;src/sys.rs&lt;/code&gt;, &lt;code&gt;src/ring.rs&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The lowest layer handles raw kernel communication. No OS library wrappers. No abstraction. RingCore manually invokes &lt;code&gt;SYS_IO_URING_SETUP&lt;/code&gt; and &lt;code&gt;SYS_IO_URING_ENTER&lt;/code&gt; via &lt;code&gt;libc&lt;/code&gt;, and uses &lt;code&gt;mmap&lt;/code&gt; to map the kernel's SQ and CQ ring buffers directly into the process's address space.&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;// Manually invoke the io_uring_setup syscall&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ring_fd&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="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;syscall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SYS_io_uring_setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;QUEUE_DEPTH&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;c_long&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;params&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;c_long&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;as&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Map the Submission Queue into our address space&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sq_ptr&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="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&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;ptr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;null_mut&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;sq_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PROT_READ&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PROT_WRITE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MAP_SHARED&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MAP_POPULATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ring_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IORING_OFF_SQ_RING&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;off_t&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 is the part most async tutorials skip entirely. In RingCore, it's front and center.&lt;/p&gt;




&lt;h3&gt;
  
  
  Layer 2: Wrapping Operations in Futures (&lt;code&gt;src/op.rs&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This is where things get interesting. Rust's &lt;code&gt;Future&lt;/code&gt; trait is simple: poll it, get &lt;code&gt;Poll::Ready(value)&lt;/code&gt; if the result is done, or &lt;code&gt;Poll::Pending&lt;/code&gt; if not along with a &lt;strong&gt;Waker&lt;/strong&gt; so someone can nudge it later.&lt;/p&gt;

&lt;p&gt;In RingCore, every &lt;code&gt;io_uring&lt;/code&gt; operation becomes a &lt;code&gt;Future&lt;/code&gt;. Here's the key poll 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="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Op&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;i32&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;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cx&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;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Poll&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If this is the first poll, submit the SQE to the ring&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.submitted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;RING&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;ring&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;ring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ring&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="c1"&gt;// Write the Submission Queue Entry to the shared kernel buffer&lt;/span&gt;
                &lt;span class="n"&gt;ring&lt;/span&gt;&lt;span class="nf"&gt;.push_sqe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.sqe&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// Store the Waker in a global map, keyed by our unique operation ID&lt;/span&gt;
            &lt;span class="c1"&gt;// The executor will retrieve this when the kernel signals completion&lt;/span&gt;
            &lt;span class="n"&gt;WAKER_MAP&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.user_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="nf"&gt;.waker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.submitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;Poll&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Pending&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Go away, we'll call you when the kernel is done&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Check if our Completion Queue Entry has arrived&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.result&lt;/span&gt;&lt;span class="nf"&gt;.take&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Poll&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nb"&gt;None&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;// Update the waker and keep waiting&lt;/span&gt;
                &lt;span class="n"&gt;WAKER_MAP&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.user_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="nf"&gt;.waker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="nn"&gt;Poll&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Pending&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The elegant part: when the kernel finishes and writes a CQE with a matching ID, the executor retrieves the stored Waker and calls it. No magic, it's just a map, an ID, and a callback.&lt;/p&gt;




&lt;h3&gt;
  
  
  Layer 3: The Executor (&lt;code&gt;src/executor.rs&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The executor is the brain that orchestrates everything. Its main loop is beautifully simple:&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;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&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="k"&gt;self&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="c1"&gt;// Step 1: Poll all tasks that have been woken up&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;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ready_queue&lt;/span&gt;&lt;span class="nf"&gt;.pop_front&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;waker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="nf"&gt;.waker&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;cx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_waker&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;waker&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;task&lt;/span&gt;&lt;span class="py"&gt;.future&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.poll&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;cx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;Poll&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&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="cm"&gt;/* Task complete, drop it */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nn"&gt;Poll&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Pending&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Task is waiting on I/O, leave it */&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="c1"&gt;// Step 2: Submit pending SQEs and harvest completed CQEs&lt;/span&gt;
        &lt;span class="c1"&gt;// min_complete=1 means: block until at least one operation finishes&lt;/span&gt;
        &lt;span class="c1"&gt;// This puts the thread to sleep until the kernel has work for us&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ring&lt;/span&gt;&lt;span class="nf"&gt;.submit_and_wait&lt;/span&gt;&lt;span class="p"&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;// Step 3: For each completed operation, wake the waiting task&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cqe&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;WAKER_MAP&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;waker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&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;cqe&lt;/span&gt;&lt;span class="py"&gt;.user_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// Store the result, then wake the future&lt;/span&gt;
                    &lt;span class="nf"&gt;store_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cqe&lt;/span&gt;&lt;span class="py"&gt;.user_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cqe&lt;/span&gt;&lt;span class="py"&gt;.res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;waker&lt;/span&gt;&lt;span class="nf"&gt;.wake&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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.all_tasks_complete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classic &lt;strong&gt;event loop&lt;/strong&gt; similar in spirit to Node.js, but with direct kernel access instead of libuv underneath.&lt;/p&gt;




&lt;h3&gt;
  
  
  Layer 4: Friendly Wrappers (&lt;code&gt;src/net.rs&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The top layer gives you &lt;code&gt;TcpListener&lt;/code&gt; and &lt;code&gt;TcpStream&lt;/code&gt; with clean &lt;code&gt;async fn&lt;/code&gt; methods. They feel like normal Rust networking but under the hood, they're submitting SQEs to the ring.&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;impl&lt;/span&gt; &lt;span class="n"&gt;TcpStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;read&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;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&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="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// This creates an Op future that submits IORING_OP_READ&lt;/span&gt;
        &lt;span class="c1"&gt;// and suspends until the kernel completes it&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Op&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&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;result&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Err&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="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw_os_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;result&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="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole stack, four files, clean separation, nothing hidden.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Benchmarks
&lt;/h2&gt;

&lt;p&gt;Tested on Debian 13, Kernel 6.12. Comparing RingCore against &lt;code&gt;std&lt;/code&gt; and Tokio.&lt;/p&gt;

&lt;h3&gt;
  
  
  File I/O : reading a 100MB file
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Real Time&lt;/th&gt;
&lt;th&gt;System Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;std::fs&lt;/code&gt; (synchronous)&lt;/td&gt;
&lt;td&gt;0.057s&lt;/td&gt;
&lt;td&gt;0.016s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokio (epoll + thread pool)&lt;/td&gt;
&lt;td&gt;0.461s&lt;/td&gt;
&lt;td&gt;0.376s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RingCore (io_uring)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.088s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.036s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tokio is &lt;strong&gt;5× slower&lt;/strong&gt; here. Why? Tokio doesn't use &lt;code&gt;io_uring&lt;/code&gt; for file I/O by default, it offloads blocking file reads to a thread pool, which adds significant overhead. RingCore uses true async kernel operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Networking : sequential and concurrent requests
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Case&lt;/th&gt;
&lt;th&gt;std (threaded)&lt;/th&gt;
&lt;th&gt;Tokio (epoll)&lt;/th&gt;
&lt;th&gt;RingCore (io_uring)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100 sequential requests&lt;/td&gt;
&lt;td&gt;12.8ms&lt;/td&gt;
&lt;td&gt;14.9ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7.5ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000 concurrent requests&lt;/td&gt;
&lt;td&gt;48.3ms&lt;/td&gt;
&lt;td&gt;1,080ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;67.9ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 1,000-request stress test is the eye-opener. Tokio takes over a second because its thread-per-task model drowns in scheduling overhead at scale. RingCore handles all of it on a &lt;strong&gt;single thread&lt;/strong&gt;, with the kernel doing the heavy lifting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced: kernel-level task chaining
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;IOSQE_IO_LINK&lt;/code&gt;, RingCore chains dependent operations (like Read → Write) so the kernel executes them back-to-back without ever returning to userspace. One &lt;code&gt;io_uring_enter&lt;/code&gt; call. Zero ping-pong.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mental Model That Changes Everything
&lt;/h2&gt;

&lt;p&gt;Here's what building RingCore made concrete for me, the thing no tutorial made clear before:&lt;/p&gt;

&lt;p&gt;When you &lt;code&gt;.await&lt;/code&gt; something in Rust, you're saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I'm not ready yet. Here's my callback (the Waker). Come get me when something changes."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The executor moves on to other tasks. The kernel works in the background. When the kernel is done, it writes a CQE. The executor reads it, finds the matching Waker in the map, and calls it. Your task wakes up and continues from where it left off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's the entire model.&lt;/strong&gt; RingCore makes every step of it visible and there's no layer you can't read.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in the Repo
&lt;/h2&gt;

&lt;p&gt;Examples are organized into four tiers so you can explore progressively:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 1 : Proving the runtime&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;          &lt;span class="c"&gt;# Chained Accept → Read → Write&lt;/span&gt;
cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &amp;lt;file&amp;gt; &lt;span class="c"&gt;# File I/O in isolation&lt;/span&gt;
cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; timer         &lt;span class="c"&gt;# Task parking and waking without I/O&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tier 2 : The async model&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; concurrent_downloads  &lt;span class="c"&gt;# 100 SQEs submitted simultaneously&lt;/span&gt;
cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; timeout_race          &lt;span class="c"&gt;# Operation cancellation via IORING_OP_ASYNC_CANCEL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tier 3 : Real workloads&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; http_server   &lt;span class="c"&gt;# High-concurrency "Hello World"&lt;/span&gt;
cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; file_server   &lt;span class="c"&gt;# Serving static files over TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tier 4 : Advanced features&lt;/strong&gt;&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="nb"&gt;sudo &lt;/span&gt;cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; sqpoll         &lt;span class="c"&gt;# Kernel-side SQ polling (needs CAP_SYS_ADMIN)&lt;/span&gt;
cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; linked_cat &lt;span class="nt"&gt;--&lt;/span&gt; &amp;lt;file&amp;gt; &lt;span class="c"&gt;# Chained Read + Write at kernel level&lt;/span&gt;
cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; multishot_accept    &lt;span class="c"&gt;# One SQE → infinite connection CQEs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start with &lt;code&gt;echo&lt;/code&gt;, trace through the source, and you'll have a complete mental model of async I/O in about an afternoon.&lt;/p&gt;




&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linux 5.10+&lt;/strong&gt; for stable &lt;code&gt;IORING_OP_ACCEPT&lt;/code&gt; support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;x86_64&lt;/strong&gt; architecture&lt;/li&gt;
&lt;li&gt;Dependencies: &lt;code&gt;libc&lt;/code&gt; and &lt;code&gt;std&lt;/code&gt; only
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;ringcore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Build This Instead of Just Using Tokio?
&lt;/h2&gt;

&lt;p&gt;Tokio is the right choice for production. I'm not suggesting you replace it.&lt;/p&gt;

&lt;p&gt;But if you've ever stared at a &lt;code&gt;select!&lt;/code&gt; macro, a &lt;code&gt;JoinHandle&lt;/code&gt;, or a &lt;code&gt;.await&lt;/code&gt; and wondered &lt;em&gt;what is actually happening in the kernel right now&lt;/em&gt;, building something like RingCore is the answer.&lt;/p&gt;

&lt;p&gt;I'm not intimidated by async Rust anymore. Not because it got simpler, but because I can now see every moving part. The abstraction didn't disappear, I just understand what it's abstracting.&lt;/p&gt;




&lt;h2&gt;
  
  
  This is Part of a Series
&lt;/h2&gt;

&lt;p&gt;RingCore isn't the first time I've gone down this rabbit hole. A few weeks ago I also built a &lt;a href="https://github.com/sumant1122/Nucleus" rel="noopener noreferrer"&gt;container engine in Rust that starts in 10ms&lt;/a&gt;, cracking open Linux namespaces, cgroups, and &lt;code&gt;clone()&lt;/code&gt; syscalls along the way.&lt;/p&gt;

&lt;p&gt;The two projects rhyme. With the container engine I asked: &lt;em&gt;what actually happens when you run a container?&lt;/em&gt; With RingCore I asked: &lt;em&gt;what actually happens when you &lt;code&gt;.await&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both answers live in the kernel. Both are learnable. The best way to demystify them is to build a tiny, intentionally incomplete version yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/sumant1122/ringcore" rel="noopener noreferrer"&gt;github.com/sumant1122/ringcore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crates.io:&lt;/strong&gt; &lt;a href="https://crates.io/crates/ringcore" rel="noopener noreferrer"&gt;crates.io/crates/ringcore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://docs.rs/ringcore" rel="noopener noreferrer"&gt;docs.rs/ringcore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Companion project (container engine):&lt;/strong&gt; &lt;a href="https://github.com/sumant1122/Nucleus" rel="noopener noreferrer"&gt;github.com/sumant1122/Nucleus&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this sparked any curiosity about systems programming, async I/O, or Rust internals, that was the whole point. Issues and PRs are very welcome.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>linux</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
