<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://zacjw.com/feed/all_posts.xml" rel="self" type="application/atom+xml" /><link href="https://zacjw.com/" rel="alternate" type="text/html" /><updated>2026-05-11T11:59:05+00:00</updated><id>https://zacjw.com/feed/all_posts.xml</id><title type="html">ZacJW | /feed/all_posts.xml</title><subtitle>A site where I write about my projects</subtitle><entry><title type="html">Building a webmentions receiver</title><link href="https://zacjw.com/blog/2026/05/10/Webmentions-receiver" rel="alternate" type="text/html" title="Building a webmentions receiver" /><published>2026-05-10T00:00:00+00:00</published><updated>2026-05-10T00:00:00+00:00</updated><id>https://zacjw.com/blog/2026/05/10/Webmentions-receiver</id><content type="html" xml:base="https://zacjw.com/blog/2026/05/10/Webmentions-receiver"><![CDATA[<p>For the last few years at least, I’ve been pretty opposed to the idea of websites having public comments sections. I think the context online is very different from that of an in-person conversation with a group of people you know, and so I think online public comments go very easily awry. I recently got around to looking into webmentions since they seem much healthier as a discourse mechanism, and have since decided that I will add support for them to this site.</p>

<p>Adding support for receiving webmentions seems like a good place to start. This site is a static site built with Jekyll and I want it to stay that way. Since <a href="https://www.w3.org/TR/webmention/#h-sender-discovers-receiver-webmention-endpoint">the standard allows for the receiver endpoint to be a different server</a>, I thought I’d make a separate application to run on my homeserver. I have a private repo for the site on <a href="https://forgejo.zacjw.com/">my Forgejo instance</a> so it would need to make commits and pull requests there which I could then merge to trigger CI to update the site.</p>

<p>Rust is my go-to language nowadays and seems like a reasonable fit for this kind of project. The applications consists of a single endpoint HTTP server which handles incoming requests, parses them, does some basic validation and then queues them for processing by a worker task which does more in-depth validation and communicates with Forgejo.</p>

<p>Altogether, it’s less than 1,000 lines of code and less than a day of work to get something that I’m happy with. The whole thing is built into a docker container and is now running happily on my homeserver. If you’d like to checkout the code you can find the repo at <a href="https://forgejo.zacjw.com/zac/webmention-receiver">https://forgejo.zacjw.com/zac/webmention-receiver</a></p>

<p>Next step will be actually integrating it into the site so the files it creates become links at the end of posts. I’ll also add a little form into each post for adding a webmention in case your site doesn’t automatically send webmentions.</p>]]></content><author><name></name></author><category term="web" /><category term="webmentions" /><summary type="html"><![CDATA[For the last few years at least, I’ve been pretty opposed to the idea of websites having public comments sections. I think the context online is very different from that of an in-person conversation with a group of people you know, and so I think online public comments go very easily awry. I recently got around to looking into webmentions since they seem much healthier as a discourse mechanism, and have since decided that I will add support for them to this site.]]></summary></entry><entry><title type="html">Jekyll-feed combined feeds</title><link href="https://zacjw.com/blog/2026/05/10/2026-05-10-Jekyll-feed-combined-feeds" rel="alternate" type="text/html" title="Jekyll-feed combined feeds" /><published>2026-05-10T00:00:00+00:00</published><updated>2026-05-10T00:00:00+00:00</updated><id>https://zacjw.com/blog/2026/05/10/2026-05-10%20Jekyll-feed%20combined%20feeds</id><content type="html" xml:base="https://zacjw.com/blog/2026/05/10/2026-05-10-Jekyll-feed-combined-feeds"><![CDATA[<p>I wanted to add support for an atom feed for this site, but my use of Jekyll is a bit unconventional and so jekyll-feed couldn’t quite do what I needed. I needed a way to generate a single feed from multiple collections. This was <a href="https://github.com/jekyll/jekyll-feed/issues/390">requested by a few people on GitHub</a> but it doesn’t seem to have been implemented so I thought I would have a go.</p>

<p>You can find my fork with this feature at <a href="https://forgejo.zacjw.com/zac/jekyll-feed">https://forgejo.zacjw.com/zac/jekyll-feed</a></p>

<p>Use of the new feature is pretty simple. Here’s an excerpt of this site’s <code class="language-plaintext highlighter-rouge">_config.yml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">feed</span><span class="pi">:</span>
  <span class="na">combined_feeds</span><span class="pi">:</span>
    <span class="na">all_posts</span><span class="pi">:</span>
      <span class="na">collections</span><span class="pi">:</span>
        <span class="na">blog</span><span class="pi">:</span>
        <span class="na">projects</span><span class="pi">:</span>
          <span class="na">except</span><span class="pi">:</span>
            <span class="na">is_top_level</span><span class="pi">:</span> <span class="kc">true</span>
</code></pre></div></div>

<p>This also shows off that I added a filtering feature to the combined feeds since I needed a way to exclude certain project posts from the feed. I’ve not tried upstreaming this yet (I feel like the filtering feature is pretty niche so they might not accept it), but maybe I’ll do that at some point.</p>]]></content><author><name></name></author><category term="web" /><category term="jekyll" /><category term="rss" /><category term="atom" /><summary type="html"><![CDATA[I wanted to add support for an atom feed for this site, but my use of Jekyll is a bit unconventional and so jekyll-feed couldn’t quite do what I needed. I needed a way to generate a single feed from multiple collections. This was requested by a few people on GitHub but it doesn’t seem to have been implemented so I thought I would have a go.]]></summary></entry><entry><title type="html">Introduction to zssg</title><link href="https://zacjw.com/projects/zssg/zssg-into" rel="alternate" type="text/html" title="Introduction to zssg" /><published>2026-05-05T00:00:00+00:00</published><updated>2026-05-05T00:00:00+00:00</updated><id>https://zacjw.com/projects/zssg/zssg-into</id><content type="html" xml:base="https://zacjw.com/projects/zssg/zssg-into"><![CDATA[<p>This site is written with Jekyll as its static site generator. It works well enough but there are aspects of it that I wish were different. I’d like to make my own that prioritises the things I care about, and this project will document that effort.</p>

<h2 id="history-of-this-website">History of this website</h2>

<p>This website wasn’t always written in Jekyll. When I started this site I didn’t even know about static site generators. The thought of ‘compiling’ a HTML page didn’t cross my mind. I assumed the only option besides manually authoring each page was to do something completely dynamic. It’s for this reason that I started this site on top of Wordpress.</p>

<p>Wordpress was functional. I liked being able to write posts in the browser, but it was never a must have feature for me. Eventually I got fed up with the continuous stream of security patches that had to be applied and decided to rebuild the site.</p>

<p>At this point I still didn’t know about static site generators so I decided to build my only dynamic site generator. It was a few PHP scripts and a few snippets of HTML. It didn’t really work well. I never even tried making an in browser editor for posts. Turns out my attempt was just as insecure as Wordpress, so I gave up on it after a while.</p>

<p>The I discovered GitHub Pages and Jekyll. It was eye-opening. I hadn’t considered adding a build step and then just serving static files. I threw away the existing themes and jumped right into custom layouts to make sites that worked how I wanted. I eventually applied what I had learnt to this site, though I never did write very much for it.</p>

<p>Recently I had a look at Amos Wenger’s dodeca static site generator. He generally does really good work so I was expecting it to be pretty smooth to build a new site with it. It was anything but. The templating engine seems completely unable to express basic conditionals like ‘if the <code class="language-plaintext highlighter-rouge">[extra]</code> section of this post’s front matter has a particular key defined, render this section’. Instead I would get errors telling me the key wasn’t defined in the very condition where I was trying to check if it was. I promptly gave up and decided to build a new site but still in Jekyll.</p>

<p>This time I did more work with custom plugins to let me group certain posts into projects and have a nice navigation experience. I also spent more effort on how the site looks but that could have been done in any competent SSG.</p>

<h2 id="my-priorities-for-a-static-site-generator">My Priorities for a Static Site Generator</h2>

<p>Beyond the fundamentals of a permissive templating engine, my single highest priority is <strong>plugins</strong>. Plugins make it possible to customise the SSG beyond what can be done within the templating language. I’ve already mentioned how I use a custom plugin to support grouping posts into projects, but I also use it for the recommendations at the end of blog posts. Each post has a list of tags in its front matter, and it just looks for other posts with lots of tags in common. I couldn’t come up with a way to do it in liquid, but it was all of about 30 lines of Ruby.</p>

<p>Dodeca doesn’t have plugins (at least not at the time of writing). It does have ‘Build Steps’ but, as far as I can tell, they’re just a nice way to hook a command to be run at build time and reference the output from your templates. It can’t transform posts between parsing and rendering, or inject synthetic posts.</p>

<p>I did also look at Cobalt after the bad experience with Dodeca, but it too doesn’t offer a plugin system, and at this point I consider one a need-to-have.</p>

<h2 id="building-a-static-site-generator-where-the-plugin-system-is-the-headline-feature">Building a static site generator where the plugin system is the headline feature</h2>

<p>What might a static site generator look like if it were built around its plugin system. I think it ought to be fairly agnostic over the language used for the plugins. I managed to use Ruby for the plugins I needed to write for Jekyll, but it was complicated by me never having used the language before. I don’t have any interest in Ruby as a language so if Jekyll gave me more options I probably would have chosen something else.</p>

<p>If plugins are a first-class feature, it should be easy to bring in 3rd party plugins. Part of making it easy is not having to worry about malware pretending to be just the plugin you need. So plugins should be isolated from the host so they can’t steal all of your browser’s cookies and saved passwords or encrypt and ransom your files. Isolating the plugins means a plugin will have to bring its dependencies with it, but that’s probably for the best since it will improve compatibility.</p>

<p>On the topic of compatibility, ARM CPUs are becoming more common (and RISC V might get there eventually too) so plugins should be agnostic of host CPU architecture.</p>

<p>This all points to running the plugins in some kind of thin architecture-agnostic virtual machine that can be targeted by many languages. Web Assembly is a thin architecture-agnostic virtual machine that can be targeted by many languages. I’m hardly the first to imagine using Web Assembly for this purpose; the <a href="https://extism.org/">Extism project</a> looks perfect for this. Looking more closely at if it can handle fine-grained isolation, I’m not sure it can so I can’t just use it as-is but I can at least use it as a guide.</p>

<p>A good way to make sure that the plugin system is sufficiently powerful might be to actually make every part of it beyond the plugin runtime itself a plugin. It’ll end up being very modular, like a build-your-own-SSG toolkit.</p>

<p>The rest of this project will document building such a static site generator and hopefully by the end of it I’ll have a tool worth rewriting my site in yet again.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This site is written with Jekyll as its static site generator. It works well enough but there are aspects of it that I wish were different. I’d like to make my own that prioritises the things I care about, and this project will document that effort.]]></summary></entry><entry><title type="html">Till: Part 2</title><link href="https://zacjw.com/projects/till/TillExecutor2" rel="alternate" type="text/html" title="Till: Part 2" /><published>2024-01-08T00:00:00+00:00</published><updated>2024-01-08T00:00:00+00:00</updated><id>https://zacjw.com/projects/till/TillExecutor2</id><content type="html" xml:base="https://zacjw.com/projects/till/TillExecutor2"><![CDATA[<p>In this part I’ll be implementing the traits written in the first part, and then testing it on an Arduino nano.</p>

<h2 id="implementation">Implementation</h2>

<h3 id="marshall">Marshall</h3>

<p>I’ll introduce the <code class="language-plaintext highlighter-rouge">Marshall</code> implementation first since it’s quite short but does have some details worth pointing out.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cd">/// An executor marshall that is only safe to be used from a single thread.</span>
<span class="cd">///</span>
<span class="cd">/// Its implementation of [Sync] is only so that you can have static values.</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">SingleThreadMarshall</span> <span class="p">{</span>
    <span class="n">ptr</span><span class="p">:</span> <span class="n">Cell</span><span class="o">&lt;*</span><span class="k">const</span> <span class="n">Cell</span><span class="o">&lt;</span><span class="nb">bool</span><span class="o">&gt;&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1">// SAFETY: creating values of this type is unsafe and requires upholding a</span>
<span class="c1">// contract that ensures no UB can occur due to what would otherwise be an</span>
<span class="c1">// unsound impl of Sync for this type.</span>
<span class="k">unsafe</span> <span class="k">impl</span> <span class="nb">Sync</span> <span class="k">for</span> <span class="n">SingleThreadMarshall</span> <span class="p">{}</span>

<span class="k">impl</span> <span class="n">SingleThreadMarshall</span> <span class="p">{</span>
    <span class="cd">/// You must not share references to this value between threads. All method calls</span>
    <span class="cd">/// on this value must come from the same thread for its entire lifetime.</span>
    <span class="cd">///</span>
    <span class="cd">/// If this was called outside of a static initialiser, the value can only be used</span>
    <span class="cd">/// from the thread that created it.</span>
    <span class="cd">///</span>
    <span class="cd">/// Its implementation of [Sync] is only so that you can have static values.</span>
    <span class="k">pub</span> <span class="k">const</span> <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span> <span class="p">{</span>
            <span class="n">ptr</span><span class="p">:</span> <span class="nn">Cell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">core</span><span class="p">::</span><span class="nn">ptr</span><span class="p">::</span><span class="nf">null</span><span class="p">()),</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">register</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Cell</span><span class="o">&lt;</span><span class="nb">bool</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.ptr</span><span class="nf">.set</span><span class="p">(</span><span class="n">b</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">unregister</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.ptr</span><span class="nf">.set</span><span class="p">(</span><span class="nn">core</span><span class="p">::</span><span class="nn">ptr</span><span class="p">::</span><span class="nf">null</span><span class="p">());</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Unfortunately this implementation ends up with some <code class="language-plaintext highlighter-rouge">unsafe</code> and worse, a dangerous unsafe <code class="language-plaintext highlighter-rouge">Sync</code> impl for a type that clearly can’t be shared between threads. This is an example of Rust’s assumptions about the target environment being wrong and then subsequently constraining us more tightly than is actually necessary for our target.</p>

<p>In Rust, <code class="language-plaintext highlighter-rouge">static</code> variables must be of types that implement <code class="language-plaintext highlighter-rouge">Sync</code> since the language can’t (or at least doesn’t) restrict access by thread. That lets us ‘fearlessly’ add multithreading to our programs without risk of UB, but it’s not a free lunch. Our types have to be designed to actually be thread-safe and that’s on us. Since my goal is to test on an AtMega328, the <code class="language-plaintext highlighter-rouge">Sync</code> requirement is <strong>all downside, no upside</strong> as there’s no threading on that target anyway.</p>

<blockquote>
  <p>You may wonder why I don’t use <code class="language-plaintext highlighter-rouge">thread_local!</code> since that lets you avoid the <code class="language-plaintext highlighter-rouge">Sync</code> requirement. It’s because it’s not part of <code class="language-plaintext highlighter-rouge">core</code>, it’s part of <code class="language-plaintext highlighter-rouge">std</code>, and goal 1 was to support <code class="language-plaintext highlighter-rouge">no-std</code> targets.</p>
</blockquote>

<p>This boxes me into the <code class="language-plaintext highlighter-rouge">unsafe impl</code> with little alternative. To try to make it a bit clearer how dangerous this type is for threaded targets, I’ve made the <code class="language-plaintext highlighter-rouge">new</code> method <code class="language-plaintext highlighter-rouge">unsafe</code> with a doc comment that describes a contract that undoes the <code class="language-plaintext highlighter-rouge">Sync</code>.</p>

<p>With that annoying detail out of the way, we can talk about why <code class="language-plaintext highlighter-rouge">register</code> and <code class="language-plaintext highlighter-rouge">unregister</code> are also unsafe. The marshall’s job is to provide a <code class="language-plaintext highlighter-rouge">'static</code> lifetime stepping stone for a waker to send a signal to a task manager. The task manager might have a lifetime shorter than <code class="language-plaintext highlighter-rouge">'static</code> so the only way to keep the marshall <code class="language-plaintext highlighter-rouge">'static</code> is to unsafely extend the reference lifetime or equivalently use a raw pointer. The task manager could be destroyed or moved while a waker tied to this marshall still exists, which in the <code class="language-plaintext highlighter-rouge">&amp;'static</code> case would be instant UB and in the raw pointer case would be UB when the waker attempts to signal the task manager. Making registering a task manager with the marshall <code class="language-plaintext highlighter-rouge">unsafe</code> makes it clear that it is the programmers responsibility to unregister before moving or destroying the task manager.</p>

<p>This probably could be simplified with some sort of registration handle which borrows the task manager and unregisters it from the marshall when the handle is dropped, but it would probably have to involve pinning to guarantee that it is actually dropped and not forgotten. It might also cause issues with not being able to mutably borrow the task manager for anything else, so for now this manual and more error-prone approach will have to do.</p>

<p>The implementation of the <code class="language-plaintext highlighter-rouge">Marshall</code> trait is straightforward:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">Marshall</span> <span class="k">for</span> <span class="n">SingleThreadMarshall</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">wake</span><span class="p">(</span><span class="o">&amp;</span><span class="k">'static</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">ptr</span> <span class="o">=</span> <span class="k">self</span><span class="py">.ptr</span><span class="nf">.get</span><span class="p">();</span>
        <span class="k">if</span> <span class="n">ptr</span><span class="nf">.is_null</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="p">(</span><span class="o">&amp;*</span><span class="n">ptr</span><span class="p">)</span><span class="nf">.set</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">waker</span><span class="p">(</span><span class="o">&amp;</span><span class="k">'static</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="n">Waker</span> <span class="p">{</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="nn">Waker</span><span class="p">::</span><span class="nf">from_raw</span><span class="p">(</span><span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="nn">RawWaker</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span>
                <span class="k">self</span> <span class="k">as</span> <span class="o">*</span><span class="k">const</span> <span class="k">Self</span> <span class="k">as</span> <span class="o">*</span><span class="k">const</span> <span class="p">(),</span>
                <span class="o">&amp;</span><span class="n">SINGLE_THREAD_MARSHALL_VTABLE</span><span class="p">,</span>
            <span class="p">))</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">SINGLE_THREAD_MARSHALL_VTABLE</code> just calls the marshall’s <code class="language-plaintext highlighter-rouge">wake</code> method for both <code class="language-plaintext highlighter-rouge">wake</code> and <code class="language-plaintext highlighter-rouge">wake_by_ref</code>.</p>

<h3 id="implementing-fusedfuturewithwakestatus">Implementing <code class="language-plaintext highlighter-rouge">FusedFutureWithWakeStatus</code></h3>

<p>In the previous section I talked about how the marshall signals the task manager, but that isn’t really true. It signals the task directly as it is the task which must hold its wake status (that’s what <code class="language-plaintext highlighter-rouge">FusedFutureWithWakeStatus</code> does). It’s worth introducing an implementation of <code class="language-plaintext highlighter-rouge">FusedFutureWithWakeStatus</code> to make that more clear.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[pin_project::pin_project]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">SingleThreadWithWakeStatus</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="nd">#[pin]</span>
    <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">,</span>
    <span class="n">status</span><span class="p">:</span> <span class="nn">core</span><span class="p">::</span><span class="nn">cell</span><span class="p">::</span><span class="n">Cell</span><span class="o">&lt;</span><span class="nb">bool</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span> <span class="n">SingleThreadWithWakeStatus</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">register</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">marshall</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="n">SingleThreadMarshall</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">projection</span> <span class="o">=</span> <span class="k">self</span><span class="nf">.project</span><span class="p">();</span>
        <span class="n">marshall</span><span class="nf">.register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">projection</span><span class="py">.status</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice that this implementation is specifically tied to <code class="language-plaintext highlighter-rouge">SingleThreadMarshall</code> as they both agree on using a <code class="language-plaintext highlighter-rouge">Cell&lt;bool&gt;</code> for storing the wake status.</p>

<p>We then have to implement the future traits for this type:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">F</span><span class="p">:</span> <span class="n">Future</span><span class="o">&gt;</span> <span class="n">Future</span> <span class="k">for</span> <span class="n">SingleThreadWithWakeStatus</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Output</span> <span class="o">=</span> <span class="nn">F</span><span class="p">::</span><span class="n">Output</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">poll</span><span class="p">(</span>
        <span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">cx</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="n">Poll</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Output</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">projection</span> <span class="o">=</span> <span class="k">self</span><span class="nf">.project</span><span class="p">();</span>
        <span class="n">projection</span><span class="py">.f</span><span class="nf">.poll</span><span class="p">(</span><span class="n">cx</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">F</span><span class="p">:</span> <span class="n">FusedFuture</span><span class="o">&gt;</span> <span class="n">FusedFuture</span> <span class="k">for</span> <span class="n">SingleThreadWithWakeStatus</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">is_terminated</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">bool</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.f</span><span class="nf">.is_terminated</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">F</span><span class="p">:</span> <span class="n">FusedFuture</span><span class="o">&gt;</span> <span class="n">FusedFutureWithWakeStatus</span> <span class="k">for</span> <span class="n">SingleThreadWithWakeStatus</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">status</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">WakeStatus</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">self</span><span class="py">.status</span><span class="nf">.get</span><span class="p">()</span> <span class="p">{</span>
            <span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Woken</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Asleep</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">set_status</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">status</span><span class="p">:</span> <span class="n">WakeStatus</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">match</span> <span class="n">status</span> <span class="p">{</span>
            <span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Woken</span> <span class="k">=&gt;</span> <span class="k">self</span><span class="py">.status</span><span class="nf">.set</span><span class="p">(</span><span class="k">true</span><span class="p">),</span>
            <span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Asleep</span> <span class="k">=&gt;</span> <span class="k">self</span><span class="py">.status</span><span class="nf">.set</span><span class="p">(</span><span class="k">false</span><span class="p">),</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Finally an extension trait can make it a bit more ergonomic to set tasks up:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">trait</span> <span class="n">FusedFutureExt</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">with_wake_status_st</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">SingleThreadWithWakeStatus</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="k">Self</span><span class="p">:</span> <span class="nb">Sized</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">F</span><span class="p">:</span> <span class="nn">futures</span><span class="p">::</span><span class="nn">future</span><span class="p">::</span><span class="n">FusedFuture</span><span class="o">&gt;</span> <span class="n">FusedFutureExt</span> <span class="k">for</span> <span class="n">F</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">with_wake_status_st</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">SingleThreadWithWakeStatus</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="n">SingleThreadWithWakeStatus</span> <span class="p">{</span>
            <span class="n">f</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span>
            <span class="n">status</span><span class="p">:</span> <span class="nn">core</span><span class="p">::</span><span class="nn">cell</span><span class="p">::</span><span class="nn">Cell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="k">true</span><span class="p">),</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="task-manager">Task Manager</h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">struct</span> <span class="n">ArrayTaskManager</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="k">const</span> <span class="n">N</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">MarshallType</span><span class="p">:</span> <span class="n">Marshall</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="n">tasks</span><span class="p">:</span> <span class="p">[(</span>
        <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="k">dyn</span> <span class="n">FusedFutureWithWakeStatus</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="k">'static</span> <span class="n">MarshallType</span><span class="p">,</span>
    <span class="p">);</span> <span class="n">N</span><span class="p">],</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here’s a simple task manager type. It doesn’t support spawning tasks since it has a const generic fixed capacity. It also doesn’t own the tasks, but instead holds pinned mutable references to them. This makes everything a consistent size, without which we wouldn’t be able to use an array in this way. Each task must have its own marshall so tasks is an array of task-marshall pairs.</p>

<p>Implementing the <code class="language-plaintext highlighter-rouge">TaskManager</code> trait is as follows:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="k">const</span> <span class="n">N</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">Marshall</span><span class="p">:</span> <span class="n">ExecutorMarshall</span><span class="o">&gt;</span> <span class="n">TaskManager</span>
    <span class="k">for</span> <span class="n">ArrayTaskManager</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">N</span><span class="p">,</span> <span class="n">Marshall</span><span class="o">&gt;</span>
<span class="p">{</span>
    <span class="k">type</span> <span class="n">Marshall</span> <span class="o">=</span> <span class="n">Marshall</span><span class="p">;</span>

    <span class="k">type</span> <span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'b</span><span class="o">&gt;</span> <span class="o">=</span> <span class="n">ArrayTaskManagerIter</span><span class="o">&lt;</span><span class="nv">'b</span><span class="p">,</span> <span class="nv">'a</span><span class="p">,</span> <span class="n">N</span><span class="p">,</span> <span class="n">Marshall</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="k">Self</span><span class="p">:</span> <span class="nv">'b</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">get_task</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span>
        <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="p">(</span>
        <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">dyn</span> <span class="n">FusedFutureWithWakeStatus</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="k">'static</span> <span class="k">Self</span><span class="p">::</span><span class="n">Marshall</span><span class="p">,</span>
    <span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.tasks</span>
            <span class="nf">.get_mut</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
            <span class="nf">.map</span><span class="p">(|(</span><span class="n">task</span><span class="p">,</span> <span class="n">marshall</span><span class="p">)|</span> <span class="p">(</span><span class="k">unsafe</span> <span class="p">{</span> <span class="nn">core</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nf">transmute</span><span class="p">(</span><span class="n">task</span><span class="nf">.as_mut</span><span class="p">())</span> <span class="p">},</span> <span class="o">*</span><span class="n">marshall</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">sleep_task</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.tasks</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
            <span class="na">.0</span>
            <span class="nf">.as_mut</span><span class="p">()</span>
            <span class="nf">.set_status</span><span class="p">(</span><span class="k">crate</span><span class="p">::</span><span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Asleep</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">sleep_all</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">task</span> <span class="k">in</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="py">.tasks</span> <span class="p">{</span>
            <span class="n">task</span><span class="na">.0</span><span class="nf">.as_mut</span><span class="p">()</span><span class="nf">.set_status</span><span class="p">(</span><span class="k">crate</span><span class="p">::</span><span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Asleep</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="n">tasks</span><span class="o">&lt;</span><span class="nv">'b</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">'b</span> <span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">::</span><span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'b</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="n">ArrayTaskManagerIter</span> <span class="p">{</span>
            <span class="n">array</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="py">.tasks</span><span class="p">,</span>
            <span class="n">i</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I was running into all kinds of variance problems when writing this (the <code class="language-plaintext highlighter-rouge">ArrayTaskManagerIter</code> implementation which I’ve omitted has more too) but besides that the implementation is unsurprising.</p>

<h2 id="testing">Testing</h2>

<p>To test it out I want to build a simple program that blinks an LED and prints over UART periodically, but those periods should be different.</p>

<h3 id="task-definitions">Task Definitions</h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">fn</span> <span class="nf">blink_task</span><span class="p">(</span><span class="k">mut</span> <span class="n">pin</span><span class="p">:</span> <span class="k">impl</span> <span class="n">OutputPin</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">loop</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">pin</span><span class="nf">.set_high</span><span class="p">();</span>
        <span class="nf">sleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span><span class="k">.await</span><span class="p">;</span>
        <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">pin</span><span class="nf">.set_low</span><span class="p">();</span>
        <span class="nf">sleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span><span class="k">.await</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">async</span> <span class="k">fn</span> <span class="nf">print_task</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span> <span class="p">{</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"i={i}"</span><span class="p">);</span>
        <span class="nf">sleep</span><span class="p">(</span><span class="mi">2500</span><span class="p">)</span><span class="k">.await</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>These tasks are very simple but should serve as a good first test, but before I can try it I need to implement the <code class="language-plaintext highlighter-rouge">sleep</code> future, as well as <code class="language-plaintext highlighter-rouge">println!</code> since that normally comes from <code class="language-plaintext highlighter-rouge">std</code>.</p>

<h3 id="sleep"><code class="language-plaintext highlighter-rouge">sleep</code></h3>

<p>I took an implementation of a Rust version of Arduino’s millis from <a href="https://github.com/Rahix/avr-hal/blob/main/examples/arduino-uno/src/bin/uno-millis.rs">avr-hal</a> to build this future around it:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[pin_project::pin_project]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">Sleep</span> <span class="p">{</span>
    <span class="n">end</span><span class="p">:</span> <span class="nb">u32</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Future</span> <span class="k">for</span> <span class="n">Sleep</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Output</span> <span class="o">=</span> <span class="p">();</span>

    <span class="k">fn</span> <span class="nf">poll</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">cx</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Poll</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Output</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">projection</span> <span class="o">=</span> <span class="k">self</span><span class="nf">.project</span><span class="p">();</span>
        <span class="k">let</span> <span class="n">now</span> <span class="o">=</span> <span class="nf">millis</span><span class="p">();</span>
        <span class="k">if</span> <span class="n">now</span><span class="nf">.wrapping_sub</span><span class="p">(</span><span class="o">*</span><span class="n">projection</span><span class="py">.end</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="o">*</span><span class="n">projection</span><span class="py">.end</span><span class="p">)</span><span class="nf">.wrapping_sub</span><span class="p">(</span><span class="n">now</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">cx</span><span class="nf">.waker</span><span class="p">()</span><span class="nf">.wake_by_ref</span><span class="p">();</span>
            <span class="nn">Poll</span><span class="p">::</span><span class="nf">Ready</span><span class="p">(())</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">cx</span><span class="nf">.waker</span><span class="p">()</span><span class="nf">.wake_by_ref</span><span class="p">();</span>
            <span class="nn">Poll</span><span class="p">::</span><span class="n">Pending</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">sleep</span><span class="p">(</span><span class="n">sleep_millis</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Sleep</span> <span class="p">{</span>
    <span class="n">Sleep</span> <span class="p">{</span> <span class="n">end</span><span class="p">:</span> <span class="nf">millis</span><span class="p">()</span> <span class="o">+</span> <span class="n">sleep_millis</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Because we are operating without an event pool the future has no way to signal to the executor what it is actually waiting for so it opts to request wake up with <code class="language-plaintext highlighter-rouge">cx.waker().wake_by_ref()</code> then return <code class="language-plaintext highlighter-rouge">Poll::Pending</code>. This will cause the executor to repeatedly poll this task until sufficient time has passed, but it will interleave polling the other tasks so not to effective block as that would defeat the purpose. This is a wasteful way to write a sleep future, but should still work.</p>

<h3 id="println"><code class="language-plaintext highlighter-rouge">println!</code></h3>

<p>Though not on the surface of particular relevance to building an executor, I though I’d include how I implemented <code class="language-plaintext highlighter-rouge">println!</code> as there are some interesting details.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">struct</span> <span class="n">GlobalSerial</span> <span class="p">{</span>
    <span class="n">serial</span><span class="p">:</span> <span class="n">UnsafeCell</span><span class="o">&lt;</span>
        <span class="nb">Option</span><span class="o">&lt;</span>
            <span class="nn">avr_hal_generic</span><span class="p">::</span><span class="nn">usart</span><span class="p">::</span><span class="n">Usart</span><span class="o">&lt;</span>
                <span class="n">Atmega</span><span class="p">,</span>
                <span class="n">USART0</span><span class="p">,</span>
                <span class="nn">avr_hal_generic</span><span class="p">::</span><span class="nn">port</span><span class="p">::</span><span class="nb">Pin</span><span class="o">&lt;</span><span class="n">Input</span><span class="p">,</span> <span class="n">PD0</span><span class="o">&gt;</span><span class="p">,</span>
                <span class="nn">avr_hal_generic</span><span class="p">::</span><span class="nn">port</span><span class="p">::</span><span class="nb">Pin</span><span class="o">&lt;</span><span class="n">Output</span><span class="p">,</span> <span class="n">PD1</span><span class="o">&gt;</span><span class="p">,</span>
                <span class="n">MHz16</span><span class="p">,</span>
            <span class="o">&gt;</span><span class="p">,</span>
        <span class="o">&gt;</span><span class="p">,</span>
    <span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">unsafe</span> <span class="k">impl</span> <span class="nb">Sync</span> <span class="k">for</span> <span class="n">GlobalSerial</span> <span class="p">{}</span>

<span class="k">impl</span> <span class="n">GlobalSerial</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">init</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">self</span><span class="p">,</span>
        <span class="n">usart</span><span class="p">:</span> <span class="nn">avr_device</span><span class="p">::</span><span class="nn">atmega328p</span><span class="p">::</span><span class="n">USART0</span><span class="p">,</span>
        <span class="n">rx</span><span class="p">:</span> <span class="nn">avr_hal_generic</span><span class="p">::</span><span class="nn">port</span><span class="p">::</span><span class="nb">Pin</span><span class="o">&lt;</span><span class="n">Input</span><span class="p">,</span> <span class="n">PD0</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">tx</span><span class="p">:</span> <span class="nn">avr_hal_generic</span><span class="p">::</span><span class="nn">port</span><span class="p">::</span><span class="nb">Pin</span><span class="o">&lt;</span><span class="n">Output</span><span class="p">,</span> <span class="n">PD1</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">baudrate</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="o">*</span><span class="k">self</span><span class="py">.serial</span><span class="nf">.get</span><span class="p">()</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="nn">avr_hal_generic</span><span class="p">::</span><span class="nn">usart</span><span class="p">::</span><span class="nn">Usart</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span>
                <span class="n">usart</span><span class="p">,</span>
                <span class="n">rx</span><span class="p">,</span>
                <span class="n">tx</span><span class="p">,</span>
                <span class="n">baudrate</span><span class="py">.into_baudrate</span><span class="p">::</span><span class="o">&lt;</span><span class="n">MHz16</span><span class="o">&gt;</span><span class="p">(),</span>
            <span class="p">))</span>
        <span class="p">};</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="nn">core</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Write</span> <span class="k">for</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="n">GlobalSerial</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">write_str</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">s</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">core</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="nb">Result</span> <span class="p">{</span>
        <span class="nn">avr_device</span><span class="p">::</span><span class="nn">interrupt</span><span class="p">::</span><span class="nf">free</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="k">-&gt;</span> <span class="nn">core</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="nb">Result</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">serial</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="k">unsafe</span> <span class="p">{</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="o">*</span><span class="k">self</span><span class="py">.serial</span><span class="nf">.get</span><span class="p">()</span> <span class="p">})</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">return</span> <span class="nf">Err</span><span class="p">(</span><span class="nn">core</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="nn">Error</span><span class="p">::</span><span class="nf">default</span><span class="p">());</span>
            <span class="p">};</span>
            <span class="k">for</span> <span class="n">byte</span> <span class="k">in</span> <span class="n">s</span><span class="nf">.as_bytes</span><span class="p">()</span> <span class="p">{</span>
                <span class="nn">nb</span><span class="p">::</span><span class="nd">block!</span><span class="p">(</span><span class="n">serial</span><span class="nf">.write</span><span class="p">(</span><span class="o">*</span><span class="n">byte</span><span class="p">))</span><span class="nf">.map_err</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">())</span><span class="o">?</span>
            <span class="p">}</span>
            <span class="nf">Ok</span><span class="p">(())</span>
        <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">static</span> <span class="n">SERIAL</span><span class="p">:</span> <span class="n">GlobalSerial</span> <span class="o">=</span> <span class="n">GlobalSerial</span> <span class="p">{</span>
    <span class="n">serial</span><span class="p">:</span> <span class="nn">UnsafeCell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nb">None</span><span class="p">),</span>
<span class="p">};</span>

</code></pre></div></div>

<p>That annoying <code class="language-plaintext highlighter-rouge">unsafe impl Sync</code> appears again. <code class="language-plaintext highlighter-rouge">avr_device::interrupt::free</code> is used to prevent UB in the event that an interrupt handler tries to print (if we were interrupted during a print we may have already acquired a mutable reference to the USART through the <code class="language-plaintext highlighter-rouge">UnsafeCell</code> but our print in the interrupt handler would then acquire the mutable reference again while the other reference still exists which is UB). The part of this implementation I like the least is probably the use of <code class="language-plaintext highlighter-rouge">nb::block!</code>, but its necessity highlights some issues with Rust’s <code class="language-plaintext highlighter-rouge">fmt</code> module.</p>

<p>Ideally I would like to write asynchronously since writing over serial can take a long time, especially at low baudrates. One approach might be to define an asynchronous write trait but we have no way to pass such an implementor to any of the formatting traits like <code class="language-plaintext highlighter-rouge">Display</code> since they take a <code class="language-plaintext highlighter-rouge">Formatter&lt;'a&gt;</code> which encapsulates a <code class="language-plaintext highlighter-rouge">&amp;'a dyn core::fmt::Write</code> among other things. We could replace the formatting traits too but then we encounter another issue, <code class="language-plaintext highlighter-rouge">core::fmt::Arguments&lt;'a&gt;</code> is almost completely opaque. We’d have to not only replace the write trait and the formatting traits, but <code class="language-plaintext highlighter-rouge">format_args!</code> as well. That’s not really practical for an initial test so for now we’ll have to settle for a synchronous blocking implementation and so <code class="language-plaintext highlighter-rouge">nb::block!</code> is here to stay.</p>

<p>The <code class="language-plaintext highlighter-rouge">println!</code> macro is then defined in terms of <code class="language-plaintext highlighter-rouge">writeln!</code> with the static <code class="language-plaintext highlighter-rouge">SERIAL</code> writer.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[macro_export]</span>
<span class="nd">macro_rules!</span> <span class="n">println</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$format_string:literal</span> <span class="nv">$</span><span class="p">(,</span> <span class="nv">$</span><span class="p">(</span><span class="nv">$name:ident</span> <span class="o">=</span> <span class="p">)</span><span class="o">?</span> <span class="nv">$e:expr</span><span class="p">)</span><span class="o">*</span> <span class="nv">$</span><span class="p">(,)</span><span class="o">?</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="nd">writeln!</span><span class="p">(</span><span class="o">&amp;</span><span class="k">crate</span><span class="p">::</span><span class="nn">serial</span><span class="p">::</span><span class="n">SERIAL</span><span class="p">,</span> <span class="nv">$format_string</span> <span class="nv">$</span><span class="p">(,</span> <span class="nv">$</span><span class="p">(</span><span class="nv">$name</span> <span class="o">=</span> <span class="p">)</span><span class="o">?</span> <span class="nv">$e</span><span class="p">)</span><span class="o">*</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span>
    <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="main"><code class="language-plaintext highlighter-rouge">main</code></h3>

<p>Here’s the rest of the code so you can see how the executor is setup:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">SLEEP_TASK_MARSHALL</span><span class="p">:</span> <span class="n">SingleThreadMarshall</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nn">SingleThreadMarshall</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span> <span class="p">};</span>

<span class="k">static</span> <span class="n">PRINT_TASK_MARSHALL</span><span class="p">:</span> <span class="n">SingleThreadMarshall</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nn">SingleThreadMarshall</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span> <span class="p">};</span>

<span class="nd">#[arduino_hal::entry]</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">!</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">peripherals</span> <span class="o">=</span> <span class="nn">Peripherals</span><span class="p">::</span><span class="nf">take</span><span class="p">()</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"Failed to get peripherals"</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">pins</span> <span class="o">=</span> <span class="nn">arduino_hal</span><span class="p">::</span><span class="nd">pins!</span><span class="p">(</span><span class="n">peripherals</span><span class="p">);</span>
    <span class="nf">millis_init</span><span class="p">(</span><span class="n">peripherals</span><span class="py">.TC0</span><span class="p">);</span>
    <span class="k">unsafe</span> <span class="p">{</span>
        <span class="n">SERIAL</span><span class="nf">.init</span><span class="p">(</span>
            <span class="n">peripherals</span><span class="py">.USART0</span><span class="p">,</span>
            <span class="n">pins</span><span class="py">.d0</span><span class="nf">.into_floating_input</span><span class="p">()</span><span class="nf">.forget_imode</span><span class="p">(),</span>
            <span class="n">pins</span><span class="py">.d1</span><span class="nf">.into_output</span><span class="p">(),</span>
            <span class="mi">115200</span><span class="p">,</span>
        <span class="p">)</span>
    <span class="p">};</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">blink_task</span> <span class="o">=</span> <span class="nn">core</span><span class="p">::</span><span class="nn">pin</span><span class="p">::</span><span class="nd">pin!</span><span class="p">(</span><span class="nf">blink_task</span><span class="p">(</span><span class="n">pins</span><span class="py">.d13</span><span class="nf">.into_output</span><span class="p">())</span>
        <span class="nf">.fuse</span><span class="p">()</span>
        <span class="nf">.with_wake_status_st</span><span class="p">());</span>
    <span class="k">unsafe</span> <span class="p">{</span> <span class="n">blink_task</span><span class="nf">.as_mut</span><span class="p">()</span><span class="nf">.register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">SLEEP_TASK_MARSHALL</span><span class="p">)</span> <span class="p">};</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">print_task</span> <span class="o">=</span> <span class="nn">core</span><span class="p">::</span><span class="nn">pin</span><span class="p">::</span><span class="nd">pin!</span><span class="p">(</span><span class="nf">print_task</span><span class="p">()</span><span class="nf">.fuse</span><span class="p">()</span><span class="nf">.with_wake_status_st</span><span class="p">());</span>
    <span class="k">unsafe</span> <span class="p">{</span> <span class="n">print_task</span><span class="nf">.as_mut</span><span class="p">()</span><span class="nf">.register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">PRINT_TASK_MARSHALL</span><span class="p">)</span> <span class="p">};</span>

    <span class="k">let</span> <span class="k">mut</span> <span class="n">tasks</span> <span class="o">=</span> <span class="n">ArrayTaskManager</span> <span class="p">{</span>
        <span class="n">tasks</span><span class="p">:</span> <span class="p">[</span>
            <span class="p">(</span><span class="n">blink_task</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">SLEEP_TASK_MARSHALL</span><span class="p">),</span>
            <span class="p">(</span><span class="n">print_task</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">PRINT_TASK_MARSHALL</span><span class="p">),</span>
        <span class="p">],</span>
    <span class="p">};</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">pool</span> <span class="o">=</span> <span class="n">DummyPool</span><span class="p">;</span>

    <span class="k">let</span> <span class="n">executor</span> <span class="o">=</span> <span class="nn">Executor</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">tasks</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">pool</span><span class="p">);</span>

    <span class="nd">println!</span><span class="p">(</span><span class="s">"Starting Execution"</span><span class="p">);</span>
    <span class="k">unsafe</span> <span class="p">{</span> <span class="nn">avr_device</span><span class="p">::</span><span class="nn">interrupt</span><span class="p">::</span><span class="nf">enable</span><span class="p">()</span> <span class="p">};</span>
    <span class="n">executor</span><span class="nf">.run_to_completion</span><span class="p">();</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Execution Complete"</span><span class="p">);</span>

    <span class="k">loop</span> <span class="p">{}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And… it works! The tasks blink the onboard LED and periodically prints over UART. I’ve omitted the implementation of <code class="language-plaintext highlighter-rouge">DummyPool</code> since it’s not interesting as it doesn’t do anything. This has proved to me that async Rust can be used even on tiny embedded systems like the AtMega328.</p>

<h2 id="next-steps">Next Steps</h2>

<p>In the next part I’ll start to build the event source pool and try using it to improve the <code class="language-plaintext highlighter-rouge">sleep</code> future and to add a task that waits for a button to be pressed before doing something else.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[In this part I’ll be implementing the traits written in the first part, and then testing it on an Arduino nano.]]></summary></entry><entry><title type="html">Till: Part 1</title><link href="https://zacjw.com/projects/till/TillExecutor1" rel="alternate" type="text/html" title="Till: Part 1" /><published>2023-11-10T00:00:00+00:00</published><updated>2023-11-10T00:00:00+00:00</updated><id>https://zacjw.com/projects/till/TillExecutor1</id><content type="html" xml:base="https://zacjw.com/projects/till/TillExecutor1"><![CDATA[<p>I’ve been reading some of <a href="https://without.boats/">Without Boats’</a> <a href="https://without.boats/blog/thread-per-core/">recent</a> <a href="https://without.boats/blog/why-async-rust/">blog</a> <a href="https://without.boats/blog/a-four-year-plan/">posts</a> about the state of async programming in Rust and it’s become a sort of motivator for me to step outside my comfort zone when it comes to programming in async Rust. I write with async Rust daily at work using the <a href="https://actix.rs">Actix-Web</a> HTTP server framework and sometimes just bare <a href="https://tokio.rs">Tokio</a> when the situation calls for it. I’ve had a vague idea of what it is that an executor does for a while, but I’ve never considered writing my own until now. This will be a series of posts detailing the development of my async executor, Till.</p>

<h2 id="goals">Goals</h2>

<p><strong>Goal 1</strong>: My goal isn’t to build a better Tokio, that seems like setting myself up for failure. Instead my goal is to do what Tokio can’t do, run on small embedded systems. I could target the ESP32 since I work with them a lot, but since you can build Rust’s <code class="language-plaintext highlighter-rouge">std</code> on top of esp-idf I wouldn’t be surprised if it could just run Tokio anyway. Instead I’ll do my testing on something much smaller, an Arduino Nano. There’s no <code class="language-plaintext highlighter-rouge">std</code> support for AVR targets since they’re much too small, and since Tokio normally runs on machines with at least tens of gigabytes of RAM, it’s probably more optimised for that kind of use so I doubt it could fit into the 2.5 kilobytes of RAM of the AtMega328.</p>

<p><strong>Goal 2</strong>: Beyond targeting tiny embedded systems without <code class="language-plaintext highlighter-rouge">std</code>, I also want to support no-<code class="language-plaintext highlighter-rouge">alloc</code> environments too. With so little memory to work with on these systems, the predictability of static and stack only are especially valuable. I want to offer extra features if <code class="language-plaintext highlighter-rouge">alloc</code> is available, but it shouldn’t be mandatory.</p>

<p><strong>Goal 3</strong>: Finally, although I’ll be testing on an Arduino Nano, the whole thing should be agnostic of what it’s running on. All of the target specific logic should be able to be injected into it as needed by the consuming application.</p>

<h2 id="design">Design</h2>

<p>The way I see it an executor needs a few main parts:</p>

<ul>
  <li>Something to hold spawned tasks without moving them around since they need to be pinned.</li>
  <li>Some kind of event loop where the executor will do IO operations on behalf of the futures running in the executor</li>
  <li>Something to facilitate communication between these two pieces when a task needs to be woken up.</li>
</ul>

<p>The executor is then just going to be a thin wrapper over these parts. Each of them will get a trait and the executor will take in implementors of these traits in its <code class="language-plaintext highlighter-rouge">new</code> method. That way the user can customise aspects of the executor’s function by implementing them on their own types.</p>

<p>The event loop part doesn’t seem like it should be strictly necessary so I’ll leave it out of my proof-of-concept and work on its design later.</p>

<h3 id="task-manager">Task Manager</h3>

<p>Initial requirements for this part are to have some way to iterate over the stored tasks so the executor can poll them, and also to have a way to put tasks to sleep. The executor can’t hold the wake status of each task since it doesn’t know how many there are and would need to allocate at runtime to do so. Goal 2 precludes this requirement so that responsibility has to be pushed onto the implementor of this trait.</p>

<p>Here’s what that trait might look like:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">trait</span> <span class="n">TaskManager</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">:</span> <span class="nb">Iterator</span><span class="o">&lt;</span>
        <span class="n">Item</span> <span class="o">=</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="k">dyn</span> <span class="n">Future</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;</span>
    <span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="k">Self</span><span class="p">:</span> <span class="nv">'a</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">get_task</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span>
        <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">dyn</span> <span class="n">Future</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;&gt;</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">sleep_task</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">);</span>
    <span class="k">fn</span> <span class="nf">sleep_all</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">);</span>
    <span class="k">fn</span> <span class="n">tasks</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">::</span><span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<blockquote>
  <p>I didn’t realise this until after I had written it, but here’s a great use for Rust’s recent MVP support for generic associated types (GATs).</p>
</blockquote>

<p>The only thing the jumps out as needing improvement here is the <code class="language-plaintext highlighter-rouge">i: usize</code> in <code class="language-plaintext highlighter-rouge">get_task</code> and <code class="language-plaintext highlighter-rouge">sleep_task</code>. The idea I had was that it should correspond with when in the iterator sequence the task gets returned, but that probably imposes too many restrictions on the implementor. I’ll continue with this for now but it’ll need some more work later.</p>

<h3 id="context">Context</h3>

<p>Looking at the <a href="https://doc.rust-lang.org/std/future/trait.Future.html"><code class="language-plaintext highlighter-rouge">Future</code></a> trait…</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">trait</span> <span class="n">Future</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Output</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">poll</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">cx</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Poll</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Output</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>…the next thing to figure out is <a href="https://doc.rust-lang.org/std/task/struct.Context.html"><code class="language-plaintext highlighter-rouge">Context&lt;'a&gt;</code></a> since I’ll need those to be able to poll the tasks. Initially it looks quite good. There’s a lifetime parameter on it so maybe this communication part mentioned earlier will be able to exist on the stack. I’ve looked at this type before and remember that there’s a <code class="language-plaintext highlighter-rouge">RawWaker</code> which is sort of like a <code class="language-plaintext highlighter-rouge">&amp;dyn WakerTrait</code> except <code class="language-plaintext highlighter-rouge">WakerTrait</code> doesn’t exist and instead you have to make the V-table manually. That <code class="language-plaintext highlighter-rouge">RawWaker</code> then gets put behind a wrapper, <code class="language-plaintext highlighter-rouge">Waker</code> for some reason that I don’t understand.</p>

<blockquote>
  <p>Updated: The <code class="language-plaintext highlighter-rouge">Wake</code> trait does actually exist, but only in <code class="language-plaintext highlighter-rouge">alloc</code> and <code class="language-plaintext highlighter-rouge">std</code>, not in <code class="language-plaintext highlighter-rouge">core</code>. It requires the use of <code class="language-plaintext highlighter-rouge">Arc</code> which makes it incompatible with <code class="language-plaintext highlighter-rouge">core</code>.</p>
</blockquote>

<p>Looking at <code class="language-plaintext highlighter-rouge">Waker</code> I notice that unlike <code class="language-plaintext highlighter-rouge">Context&lt;'a&gt;</code>, it doesn’t have a lifetime parameter. I guess if you only ever have a <code class="language-plaintext highlighter-rouge">'a</code> reference to it from a context it’s basically the same thing but then I notice it implements <code class="language-plaintext highlighter-rouge">Clone</code>. So much for having the communication part be able to live on the stack, with a <code class="language-plaintext highlighter-rouge">Waker</code> that can live forever so too must anything it references also live forever. This means I need <code class="language-plaintext highlighter-rouge">'static</code> lifetimes for the <code class="language-plaintext highlighter-rouge">Waker</code>’s references which means static or heap and since I don’t want to require <code class="language-plaintext highlighter-rouge">alloc</code>, I’ll have to keep in mind as I design that the communication part will be a <code class="language-plaintext highlighter-rouge">static</code> variable. Let’s give this communication part the name…</p>

<h3 id="marshall">Marshall</h3>

<p>I think I’ve seen this term used in the context of interrupt handlers where you want to go from a function pointer to making a method call on a particular object. This situation of having to go from a <code class="language-plaintext highlighter-rouge">'static</code> to a <code class="language-plaintext highlighter-rouge">'a</code> feels similar to me so I’ll use the same name for it.</p>

<p>Here’s the trait:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">trait</span> <span class="n">Marshall</span><span class="p">:</span> <span class="nb">Sync</span> <span class="o">+</span> <span class="k">'static</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">wake</span><span class="p">(</span><span class="o">&amp;</span><span class="k">'static</span> <span class="k">self</span><span class="p">);</span>
    <span class="k">fn</span> <span class="nf">waker</span><span class="p">(</span><span class="o">&amp;</span><span class="k">'static</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="n">Waker</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The documentation for <a href="https://doc.rust-lang.org/std/task/struct.Waker.html"><code class="language-plaintext highlighter-rouge">Waker</code></a> makes note that it implements <code class="language-plaintext highlighter-rouge">Send</code> and <code class="language-plaintext highlighter-rouge">Sync</code>. Since I think my waker will basically be a reference to an implementor of Marshall, that implementor will need to be <code class="language-plaintext highlighter-rouge">Sync</code> to be safe in multi-threaded environments. Although maybe not strictly necessary, I’ve included a <code class="language-plaintext highlighter-rouge">wake</code> method in addition to the <code class="language-plaintext highlighter-rouge">waker</code> since it was easier for me to think about it this way.</p>

<p>This trait is kept deliberately very simple, nothing about how it communicates with a task manager is present. This is because I expect the <code class="language-plaintext highlighter-rouge">TaskManager</code> implementor and <code class="language-plaintext highlighter-rouge">Marshall</code> implementor to be written as a compatible pair and it makes more sense to have the bound be on the <code class="language-plaintext highlighter-rouge">TaskManager</code> side, which now becomes:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">trait</span> <span class="n">TaskManager</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Marshall</span><span class="p">:</span> <span class="n">Marshall</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">:</span> <span class="nb">Iterator</span><span class="o">&lt;</span>
        <span class="n">Item</span> <span class="o">=</span> <span class="p">(</span>
            <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="k">dyn</span> <span class="n">Future</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;</span><span class="p">,</span>
            <span class="o">&amp;</span><span class="k">'static</span> <span class="k">Self</span><span class="p">::</span><span class="n">Marshall</span><span class="p">,</span>
        <span class="p">),</span>
    <span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="k">Self</span><span class="p">:</span> <span class="nv">'a</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">get_task</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span>
        <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="p">(</span>
        <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">dyn</span> <span class="n">Future</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="k">'static</span> <span class="k">Self</span><span class="p">::</span><span class="n">Marshall</span><span class="p">,</span>
    <span class="p">)</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">sleep_task</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">);</span>
    <span class="k">fn</span> <span class="nf">sleep_all</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">);</span>
    <span class="k">fn</span> <span class="n">tasks</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">::</span><span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The executor will need to be able to get a reference to the <code class="language-plaintext highlighter-rouge">Marshall</code> to be able to construct a <code class="language-plaintext highlighter-rouge">Context&lt;'a&gt;</code> to poll with, and depending on implementation each task may need to have it’s own <code class="language-plaintext highlighter-rouge">Marshall</code> instance so making it part of the task iterator and getter seems right.</p>

<h3 id="executor">Executor</h3>

<p>Now seems like a good point to start writing the <code class="language-plaintext highlighter-rouge">Executor</code> wrapper type. Here’s the first version:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">struct</span> <span class="n">Executor</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">Tasks</span><span class="p">:</span> <span class="n">TaskManager</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">tasks</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="n">Tasks</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For now it’s just a wrapper over a mutable reference to a <code class="language-plaintext highlighter-rouge">TaskManager</code> but I expect there to be more in here once the event loop part is built.</p>

<p>Methods on the type look like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">Tasks</span><span class="p">:</span> <span class="n">TaskManager</span><span class="o">&gt;</span> <span class="n">Executor</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">Tasks</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">tasks</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="n">Tasks</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="n">Executor</span> <span class="p">{</span> <span class="n">tasks</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">run_to_completion</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">while</span> <span class="k">self</span><span class="py">.tasks</span><span class="nf">.tasks</span><span class="p">()</span><span class="nf">.any</span><span class="p">(|(</span><span class="n">task</span><span class="p">,</span> <span class="n">_</span><span class="p">)|</span> <span class="o">!</span><span class="n">task</span><span class="nf">.is_terminated</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="k">mut</span> <span class="n">task</span><span class="p">,</span> <span class="n">marshall</span><span class="p">))</span> <span class="k">in</span>
                <span class="k">self</span><span class="py">.tasks</span><span class="nf">.tasks</span><span class="p">()</span><span class="nf">.enumerate</span><span class="p">()</span><span class="nf">.filter</span><span class="p">(|(</span><span class="n">_</span><span class="p">,</span> <span class="p">(</span><span class="n">task</span><span class="p">,</span> <span class="n">_</span><span class="p">))|</span> <span class="p">{</span>
                    <span class="nd">todo!</span><span class="p">(</span><span class="s">"What goes here?"</span><span class="p">)</span>
                <span class="p">})</span>
            <span class="p">{</span>
                <span class="k">let</span> <span class="n">waker</span> <span class="o">=</span> <span class="n">marshall</span><span class="nf">.waker</span><span class="p">();</span>
                <span class="k">let</span> <span class="k">mut</span> <span class="n">context</span><span class="p">:</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="nn">Context</span><span class="p">::</span><span class="nf">from_waker</span><span class="p">(</span><span class="o">&amp;</span><span class="n">waker</span><span class="p">);</span>
                <span class="k">self</span><span class="py">.tasks</span><span class="nf">.sleep_task</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
                <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">task</span><span class="nf">.poll</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">context</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Unfortunately, <code class="language-plaintext highlighter-rouge">run_to_completion</code> doesn’t compile. We’re borrowing from <code class="language-plaintext highlighter-rouge">self.tasks</code> for as long as we have a pinned reference to the task, so we can’t then borrow again to set the sleep status. Also, I want to be able to filter out tasks that are asleep or completed since they don’t need to be polled, but I don’t have the means to check for either. Luckily both of these issues can be solved with the same technique, use a sub-trait of <code class="language-plaintext highlighter-rouge">Future</code> for the tasks.</p>

<p>If we depend on the <a href="https://crates.io/crates/futures"><code class="language-plaintext highlighter-rouge">futures</code></a> we can use its <a href="https://docs.rs/futures/0.3.29/futures/future/trait.FusedFuture.html"><code class="language-plaintext highlighter-rouge">FusedFuture</code></a> trait so we can check if a task is complete. Similarly we can define our own sub-trait of <code class="language-plaintext highlighter-rouge">FusedFuture</code> which stores a wake status too.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">trait</span> <span class="n">FusedFutureWithWakeStatus</span><span class="p">:</span> <span class="n">FusedFuture</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">status</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">WakeStatus</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">set_status</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">status</span><span class="p">:</span> <span class="n">WakeStatus</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">WakeStatus</code> is just a two-state enum:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">enum</span> <span class="n">WakeStatus</span> <span class="p">{</span>
    <span class="n">Woken</span><span class="p">,</span>
    <span class="n">Asleep</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">set_status</code> needs to be callable when pinned so <code class="language-plaintext highlighter-rouge">Pin</code> appears in the self type. Since the <code class="language-plaintext highlighter-rouge">status</code> method is immutable it’s not needed.</p>

<p><code class="language-plaintext highlighter-rouge">TaskManager</code> now becomes:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">trait</span> <span class="n">TaskManager</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Marshall</span><span class="p">:</span> <span class="n">Marshall</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">:</span> <span class="nb">Iterator</span><span class="o">&lt;</span>
        <span class="n">Item</span> <span class="o">=</span> <span class="p">(</span>
            <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="k">dyn</span> <span class="n">FusedFutureWithWakeStatus</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;</span><span class="p">,</span>
            <span class="o">&amp;</span><span class="k">'static</span> <span class="k">Self</span><span class="p">::</span><span class="n">Marshall</span><span class="p">,</span>
        <span class="p">),</span>
    <span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="k">Self</span><span class="p">:</span> <span class="nv">'a</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">get_task</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span>
        <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="p">(</span>
        <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">dyn</span> <span class="n">FusedFutureWithWakeStatus</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="p">()</span><span class="o">&gt;&gt;</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="k">'static</span> <span class="k">Self</span><span class="p">::</span><span class="n">Marshall</span><span class="p">,</span>
    <span class="p">)</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">sleep_task</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">i</span><span class="p">:</span> <span class="nb">usize</span><span class="p">);</span>
    <span class="k">fn</span> <span class="nf">sleep_all</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">);</span>
    <span class="k">fn</span> <span class="n">tasks</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">::</span><span class="n">TaskIterator</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And <code class="language-plaintext highlighter-rouge">Executor::run_to_completion</code> becomes:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">run_to_completion</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">while</span> <span class="k">self</span><span class="py">.tasks</span><span class="nf">.tasks</span><span class="p">()</span><span class="nf">.any</span><span class="p">(|(</span><span class="n">task</span><span class="p">,</span> <span class="n">_</span><span class="p">)|</span> <span class="o">!</span><span class="n">task</span><span class="nf">.is_terminated</span><span class="p">())</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="k">mut</span> <span class="n">task</span><span class="p">,</span> <span class="n">marshall</span><span class="p">))</span> <span class="k">in</span>
            <span class="k">self</span><span class="py">.tasks</span><span class="nf">.tasks</span><span class="p">()</span><span class="nf">.enumerate</span><span class="p">()</span><span class="nf">.filter</span><span class="p">(|(</span><span class="n">_</span><span class="p">,</span> <span class="p">(</span><span class="n">task</span><span class="p">,</span> <span class="n">_</span><span class="p">))|</span> <span class="p">{</span>
                <span class="o">!</span><span class="n">task</span><span class="nf">.is_terminated</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="nd">matches!</span><span class="p">(</span><span class="n">task</span><span class="nf">.status</span><span class="p">(),</span> <span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Woken</span><span class="p">)</span>
            <span class="p">})</span>
        <span class="p">{</span>
            <span class="k">let</span> <span class="n">waker</span> <span class="o">=</span> <span class="n">marshall</span><span class="nf">.waker</span><span class="p">();</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">context</span><span class="p">:</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">core</span><span class="p">::</span><span class="nn">task</span><span class="p">::</span><span class="nn">Context</span><span class="p">::</span><span class="nf">from_waker</span><span class="p">(</span><span class="o">&amp;</span><span class="n">waker</span><span class="p">);</span>
            <span class="n">task</span><span class="nf">.as_mut</span><span class="p">()</span><span class="nf">.set_status</span><span class="p">(</span><span class="nn">WakeStatus</span><span class="p">::</span><span class="n">Asleep</span><span class="p">);</span>
            <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">task</span><span class="nf">.poll</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">context</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we’re depending on <code class="language-plaintext highlighter-rouge">FusedFuture</code> we can just discard the result of <code class="language-plaintext highlighter-rouge">task.poll</code> as it’s implementation will update termination status for us if needed.</p>

<h2 id="conclusion">Conclusion</h2>

<p>That’s it for the first part in this series. We’ve laid down the core ideas and formalised them into some traits. In the <a href="TillExecutor2">next part</a> I’ll be implementing these traits to see how they work in practise.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I’ve been reading some of Without Boats’ recent blog posts about the state of async programming in Rust and it’s become a sort of motivator for me to step outside my comfort zone when it comes to programming in async Rust. I write with async Rust daily at work using the Actix-Web HTTP server framework and sometimes just bare Tokio when the situation calls for it. I’ve had a vague idea of what it is that an executor does for a while, but I’ve never considered writing my own until now. This will be a series of posts detailing the development of my async executor, Till.]]></summary></entry><entry><title type="html">Shell scripting in Python</title><link href="https://zacjw.com/blog/2021/07/02/PythonShellScripting" rel="alternate" type="text/html" title="Shell scripting in Python" /><published>2021-07-02T00:00:00+00:00</published><updated>2021-07-02T00:00:00+00:00</updated><id>https://zacjw.com/blog/2021/07/02/PythonShellScripting</id><content type="html" xml:base="https://zacjw.com/blog/2021/07/02/PythonShellScripting"><![CDATA[<p>I don’t get along with bash scripting. The syntax is so unusual, and I need it so infrequently, that I completely forget even the basics and have to relearn from scratch every time I have to write a script. After a <a href="/blog/2021/04/14/RecreatingGHPages">recent success</a> writing what would normally be a bash script in Python instead, I thought it would be a good idea to write a library that makes operations like piping with subprocesses much easier. You can find <a href="https://github.com/ZacJW/shell-scripter">shell-scripter here on GitHub</a></p>

<p>It’s still early days for this project, but here’s a quick rundown of the features it has at time of writing:</p>

<h2 id="piping">Piping</h2>

<p>Piping is supported with <code class="language-plaintext highlighter-rouge">.pipe()</code> after a call to <code class="language-plaintext highlighter-rouge">run</code>. You can chain as many of them as you would like.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">shell_scripter</span> <span class="kn">import</span> <span class="n">run</span>
<span class="nf">run</span><span class="p">(</span><span class="sh">"</span><span class="s">dmesg</span><span class="sh">"</span><span class="p">).</span><span class="nf">pipe</span><span class="p">(</span><span class="sh">"</span><span class="s">grep</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">usb</span><span class="sh">"</span><span class="p">)().</span><span class="nf">show</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="delayed-execution">Delayed Execution</h2>

<p>You may be wondering what the <code class="language-plaintext highlighter-rouge">().show()</code> is doing on the example above. Shell-scripter doesn’t run anything until you call the command object. This allows you to build up chains bit by bit and then execute when everything is ready. <code class="language-plaintext highlighter-rouge">.show()</code> just prints the output of the command to stdout.</p>

<h2 id="background-execution">Background Execution</h2>

<p>If you want a command to execute without blocking the execution of your Python script, call the command object with <code class="language-plaintext highlighter-rouge">(False)</code> or <code class="language-plaintext highlighter-rouge">(wait=False)</code>.</p>]]></content><author><name></name></author><category term="python" /><category term="shell script" /><category term="automation" /><summary type="html"><![CDATA[I don’t get along with bash scripting. The syntax is so unusual, and I need it so infrequently, that I completely forget even the basics and have to relearn from scratch every time I have to write a script. After a recent success writing what would normally be a bash script in Python instead, I thought it would be a good idea to write a library that makes operations like piping with subprocesses much easier. You can find shell-scripter here on GitHub]]></summary></entry><entry><title type="html">Recreating GitHub Pages on my own servers</title><link href="https://zacjw.com/blog/2021/04/14/RecreatingGHPages" rel="alternate" type="text/html" title="Recreating GitHub Pages on my own servers" /><published>2021-04-14T00:00:00+00:00</published><updated>2021-04-14T00:00:00+00:00</updated><id>https://zacjw.com/blog/2021/04/14/RecreatingGHPages</id><content type="html" xml:base="https://zacjw.com/blog/2021/04/14/RecreatingGHPages"><![CDATA[<p>GitHub offers a free service to build and host Jekyll websites for a GitHub repo automatically called GitHub Pages. It’s really convenient to work with, and you can even connect your own domain names to it, but what if you don’t want to beholden to Microsoft. Here’s a guide to recreating GitHub pages, but fully self hosted.</p>

<h3 id="prerequisites">Prerequisites</h3>

<ul>
  <li>A working knowledge of git and SSH</li>
  <li>A server to be the remote git repo and to perform the Jekyll build</li>
  <li>If that server is not where you want to host the website, you’ll need some other hosting</li>
</ul>

<h3 id="how-to-setup-a-git-remote">How to setup a git remote</h3>

<p>You don’t need to install anything on your server beyond the normal git CLI and an SSH server.
On Debian/Ubuntu that’s as simple as <code class="language-plaintext highlighter-rouge">sudo apt-get install git openssh-server</code></p>

<p>Next you need to decide which user to use for the git operations. This will also be the user that the deploy script is executed as. I’ll be creating a user called <code class="language-plaintext highlighter-rouge">git</code>. That can be done with <code class="language-plaintext highlighter-rouge">sudo adduser git</code>. This makes a new user and gives them a home directory, in this case <code class="language-plaintext highlighter-rouge">/home/git/</code> which we can use to keep our repos in.</p>

<h3 id="optional-ssh-key-authentication">(Optional) SSH key authentication</h3>

<p>If you want to use key authentication rather than password authentication, we need to run <code class="language-plaintext highlighter-rouge">sudo su git</code> to get a shell as our new user, then <code class="language-plaintext highlighter-rouge">mkdir -p ~/.ssh</code> to make a directory to hold our SSH configuration files. Next run <code class="language-plaintext highlighter-rouge">chmod 700 ~/.ssh</code> so that only the git user can read and write its contents. Then create a file to hold the authorised keys with <code class="language-plaintext highlighter-rouge">nano ~/.ssh/authorized_keys</code> and copy in any public keys you want to allow to login to this user. Finally restrict it to only the git user with <code class="language-plaintext highlighter-rouge">chmod 600 ~/.ssh/authorized_keys</code></p>

<h3 id="setting-up-the-git-repo">Setting up the git repo</h3>

<p>Now that we have a user configured, we can make a git repo to hold our website. First make a folder for it with <code class="language-plaintext highlighter-rouge">mkdir -p ~/website.git</code> (you don’t have to call it website, but it’s good practice to end it with <code class="language-plaintext highlighter-rouge">.git</code>), then enter it with <code class="language-plaintext highlighter-rouge">cd ~/website.git</code> and initialise the repo with <code class="language-plaintext highlighter-rouge">git --bare init</code>. This is slightly different to how you might normally create a git repo. The <code class="language-plaintext highlighter-rouge">--bare</code> tells git not to create a working directory for it, and instead just make the files that would go in the <code class="language-plaintext highlighter-rouge">.git</code> folder directly in <code class="language-plaintext highlighter-rouge">~/website.git</code>. We do this because we aren’t going to be doing any development on this repo, that’ll be done on a local clone, so we just need the history.</p>

<p>With this we should now be able to clone it locally with <code class="language-plaintext highlighter-rouge">git clone git@your_server_domain_or_IP:website.git</code></p>

<h3 id="building-and-deploying-automatically-with-git-hooks">Building and deploying automatically with git hooks</h3>

<p>Git provides a convenient way to trigger scripts when certain events occur called hooks. In our example we can find them in <code class="language-plaintext highlighter-rouge">~/website.git/hooks/</code>. By default there are a bunch of sample scripts in there that we can ignore. Instead we are going to create a <code class="language-plaintext highlighter-rouge">~/website.git/hooks/post-receive</code> script (use whichever editor you like). I’m going to write the script in Python 3 which can be installed on your Debian/Ubuntu server with <code class="language-plaintext highlighter-rouge">sudo apt-get install python3 python3-pip</code>. If you want to deploy the website to a separate hosting over SFTP, you’ll also need to run <code class="language-plaintext highlighter-rouge">sudo pip install pysftp</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/bin/python3
</span>
<span class="kn">import</span> <span class="n">sys</span><span class="p">,</span> <span class="n">os</span><span class="p">,</span> <span class="n">subprocess</span><span class="p">,</span> <span class="n">shutil</span><span class="p">,</span> <span class="n">pysftp</span>
</code></pre></div></div>

<p>The first line here is the ‘shebang’ that tells linux that this script should be executed using the Python 3 interpreter. After that we have all the library imports that this script will use. If you don’t need to deploy over SFTP, you can remove <code class="language-plaintext highlighter-rouge">, pysftp</code> from the end of the line.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">check_refs</span><span class="p">():</span>
    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">sys</span><span class="p">.</span><span class="n">stdin</span><span class="p">:</span>
        <span class="n">old_hash</span><span class="p">,</span> <span class="n">new_hash</span><span class="p">,</span> <span class="n">ref</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">ref</span> <span class="o">==</span> <span class="sh">"</span><span class="s">refs/heads/master</span><span class="sh">"</span><span class="p">:</span>
            <span class="nf">return </span><span class="p">(</span><span class="bp">True</span><span class="p">,</span> <span class="n">old_hash</span><span class="p">,</span> <span class="n">new_hash</span><span class="p">)</span>
    <span class="nf">return </span><span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
</code></pre></div></div>

<p>Next we have a function that will check if the master branch has been updated as part of this push. If you want change which branch it is sensitive to, change <code class="language-plaintext highlighter-rouge">"refs/heads/master"</code> to <code class="language-plaintext highlighter-rouge">"refs/heads/branch_name"</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">make_temp_dirs</span><span class="p">():</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">os</span><span class="p">.</span><span class="nf">mkdir</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_checkout</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">FileExistsError</span><span class="p">:</span>
        <span class="n">shutil</span><span class="p">.</span><span class="nf">rmtree</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_checkout</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">os</span><span class="p">.</span><span class="nf">mkdir</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_checkout</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">os</span><span class="p">.</span><span class="nf">mkdir</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_build</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">FileExistsError</span><span class="p">:</span>
        <span class="n">shutil</span><span class="p">.</span><span class="nf">rmtree</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_build</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">os</span><span class="p">.</span><span class="nf">mkdir</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_build</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>This function will create temporary directories to store the checkout of the pushed branch and the results of the Jekyll build.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">git_checkout</span><span class="p">():</span>
    <span class="n">subprocess</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span>
        <span class="p">[</span><span class="sh">"</span><span class="s">git</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">--git-dir=.</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">--work-tree=/tmp/website_checkout</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">checkout</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">master</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">]</span>
    <span class="p">).</span><span class="nf">check_returncode</span><span class="p">()</span>
</code></pre></div></div>

<p>This function uses subprocess to invoke git to create a checkout of the pushed branch in our temporary directory. If you want to make it work on a different branch, change <code class="language-plaintext highlighter-rouge">"master"</code> to <code class="language-plaintext highlighter-rouge">"branch_name"</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">jekyll_build</span><span class="p">():</span>
    <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="sh">"</span><span class="s">GEM_HOME</span><span class="sh">"</span><span class="p">]</span> <span class="o">=</span> <span class="sh">"</span><span class="s">/home/git/gems</span><span class="sh">"</span>
    <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="sh">"</span><span class="s">PATH</span><span class="sh">"</span><span class="p">]</span> <span class="o">=</span> <span class="sh">"</span><span class="s">/home/git/gems/bin:</span><span class="sh">"</span> <span class="o">+</span> <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="sh">"</span><span class="s">PATH</span><span class="sh">"</span><span class="p">]</span>
    <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="sh">"</span><span class="s">BUNDLE_GEMFILE</span><span class="sh">"</span><span class="p">]</span> <span class="o">=</span> <span class="sh">"</span><span class="s">/tmp/website_checkout/Gemfile</span><span class="sh">"</span>
    <span class="n">subprocess</span><span class="p">.</span><span class="nf">run</span><span class="p">([</span><span class="sh">"</span><span class="s">bundle</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">install</span><span class="sh">"</span><span class="p">]).</span><span class="nf">check_returncode</span><span class="p">()</span>
    <span class="n">subprocess</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span>
        <span class="p">[</span><span class="sh">"</span><span class="s">bundle</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">exec</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">jekyll</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">build</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">-s</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">/tmp/website_checkout</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">-d</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">/tmp/website_build</span><span class="sh">"</span><span class="p">]</span>
    <span class="p">).</span><span class="nf">check_returncode</span><span class="p">()</span>
</code></pre></div></div>

<p>This is the function that actually performs the build. It sets up environment variables, then makes sure all build dependencies are installed, and then executes the Jekyll build with the source and destination directories set to the two temporary directories.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">rmdir</span><span class="p">(</span><span class="n">sftp</span><span class="p">:</span> <span class="n">pysftp</span><span class="p">.</span><span class="n">Connection</span><span class="p">,</span> <span class="n">path</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">sftp</span><span class="p">.</span><span class="nf">exists</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">None</span>
    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">sftp</span><span class="p">.</span><span class="nf">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">sftp</span><span class="p">.</span><span class="nf">isdir</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">p</span><span class="si">}</span><span class="sh">"</span><span class="p">):</span>
            <span class="nf">rmdir</span><span class="p">(</span><span class="n">sftp</span><span class="p">,</span> <span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">p</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">sftp</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">p</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">sftp</span><span class="p">.</span><span class="nf">rmdir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</code></pre></div></div>

<p>You’ll only need this function if you want to deploy over SFTP. The pysftp lacks the ability to recursively delete a directory, so we use this function to provide that missing functionality. This will be used to delete the old deployment before copying over the new one</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_sftp_password</span><span class="p">():</span>
    <span class="n">password_file</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">/home/git/sftp_pass</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">password</span> <span class="o">=</span> <span class="n">password_file</span><span class="p">.</span><span class="nf">read</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
    <span class="n">password_file</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">password</span>
</code></pre></div></div>

<p>Another SFTP only function. This one reads the password to use from a file.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">deploy</span><span class="p">(</span><span class="n">password</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="k">with</span> <span class="n">pysftp</span><span class="p">.</span><span class="nc">Connection</span><span class="p">(</span><span class="sh">"</span><span class="s">sftp_host_domain</span><span class="sh">"</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="sh">"</span><span class="s">sftp_user</span><span class="sh">"</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="n">password</span><span class="p">)</span> <span class="k">as</span> <span class="n">sftp</span><span class="p">:</span>
        <span class="nf">rmdir</span><span class="p">(</span><span class="n">sftp</span><span class="p">,</span> <span class="sh">"</span><span class="s">/home/sftp_user/www</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">sftp</span><span class="p">.</span><span class="nf">mkdir</span><span class="p">(</span><span class="sh">"</span><span class="s">/home/sftp_user/www</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">sftp</span><span class="p">.</span><span class="nf">put_r</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_build</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">/home/sftp_user/www</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>This function deploys the website over SFTP. You’ll need to change <code class="language-plaintext highlighter-rouge">"sftp_host_domain"</code> to be the domain of the host you want to deploy to, and any occurrences of <code class="language-plaintext highlighter-rouge">sftp_user</code> to the appropriate user. You may also need to change <code class="language-plaintext highlighter-rouge">www</code> if you want it to end up in a different directory.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">cleanup</span><span class="p">():</span>
    <span class="n">shutil</span><span class="p">.</span><span class="nf">rmtree</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_build</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">shutil</span><span class="p">.</span><span class="nf">rmtree</span><span class="p">(</span><span class="sh">"</span><span class="s">/tmp/website_checkout</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>This function is just a simple clean up function that deletes the two temporary directories once we’re done.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">should_update</span><span class="p">,</span> <span class="n">old_hash</span><span class="p">,</span> <span class="n">new_hash</span> <span class="o">=</span> <span class="nf">check_refs</span><span class="p">()</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">should_update</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Build and Deploy: nothing to do</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">None</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Updates master from </span><span class="si">{</span><span class="n">old_hash</span><span class="si">}</span><span class="s"> to </span><span class="si">{</span><span class="n">new_hash</span><span class="si">}</span><span class="s">, rebuilding Jekyll</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Making temporary dirs</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">make_temp_dirs</span><span class="p">()</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Checking out master</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">git_checkout</span><span class="p">()</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Building Jekyll</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">jekyll_build</span><span class="p">()</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Deploying via SFTP</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">deploy</span><span class="p">(</span><span class="nf">get_sftp_password</span><span class="p">())</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Cleaing up temporary dirs</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">cleanup</span><span class="p">()</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Build and Deploy complete!</span><span class="sh">"</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="nf">main</span><span class="p">()</span>
</code></pre></div></div>

<p>Lastly we have the main function that calls our functions in sequence, with some simple logging in between, and the <code class="language-plaintext highlighter-rouge">__name__ == "__main__"</code> to run our main function when the script is executed.</p>

<p>All that’s left to do is make it executable with <code class="language-plaintext highlighter-rouge">chmod +x ~/website.git/hooks/post-receive</code>, make the SFTP password file at <code class="language-plaintext highlighter-rouge">/home/git/sftp_pass</code> and set it to only be read and writeable by the git user with <code class="language-plaintext highlighter-rouge">chmod 600 ~/sftp_pass</code>.</p>

<p>You can download the full Python script <a href="/assets/2021-04-14/post-receive">here</a></p>]]></content><author><name></name></author><category term="web" /><category term="git" /><category term="python" /><category term="automation" /><category term="jekyll" /><summary type="html"><![CDATA[GitHub offers a free service to build and host Jekyll websites for a GitHub repo automatically called GitHub Pages. It’s really convenient to work with, and you can even connect your own domain names to it, but what if you don’t want to beholden to Microsoft. Here’s a guide to recreating GitHub pages, but fully self hosted.]]></summary></entry></feed>