<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="feed.xslt.xml"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Blargh</title>
    <description>The blog where I write down random techy things I've found or done.
</description>
    <link>https://blog.habets.se/</link>
    <atom:link href="https://blog.habets.se/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 11 Apr 2026 21:57:44 +0000</pubDate>
    <lastBuildDate>Sat, 11 Apr 2026 21:57:44 +0000</lastBuildDate>
    <generator>Jekyll v3.2.1</generator>
    
      <item>
        <title>Rustradio - SDR framework now also in the browser, with Wasm</title>
        <description>&lt;p&gt;I’ve previously blogged about &lt;a href=&quot;https://blog.habets.se/categories.html#rustradio&quot;&gt;RustRadio&lt;/a&gt;, my GNU Radio like
framework for writing software defined radio applications in Rust. And now
there’s more progress of an interesting kind.&lt;/p&gt;

&lt;p&gt;Anything that tries to do something similar to GNU Radio needs a few things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Core framework.&lt;/li&gt;
  &lt;li&gt;SDR components (filters, clock recovery, multipliers, etc).&lt;/li&gt;
  &lt;li&gt;A user interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to these, GNU Radio also has the excellent GNU Radio Companion for
interactive creation of flowgraphs, but I’m not tackling that yet.&lt;/p&gt;

&lt;p&gt;I have a core framework, and some components (blocks). But the UI has been a
bit lacking.&lt;/p&gt;

&lt;p&gt;I’ve played around with TUI applications, but I always knew I also wanted to
support having a UI in the browser. I’m not as interested in adding support for
QT or Windows native UI. The browser will do fine.&lt;/p&gt;

&lt;p&gt;There are two ways to get the UI in the browser:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Have the browser talk to a backend that’s running the actual DSP.&lt;/li&gt;
  &lt;li&gt;Running the DSP in the browser, with no need for a backend.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While I’ll want (1) eventually, and have some ideas about that, this post is
about running everything in the browser, using Wasm.&lt;/p&gt;

&lt;p&gt;I know that this is just scratching the surface on some aspects of this, but I
hope I at least didn’t get anything wrong.&lt;/p&gt;

&lt;h2 id=&quot;wasm-in-short&quot;&gt;Wasm, in short&lt;/h2&gt;

&lt;p&gt;There have been various technologies to running code in the browser. ActiveX,
VBScript, Java, and Flash come to mind. They’ve all gone away. Only
Javascript remains. Typescript is a better Javascript, but it’s still basically
Javascript.&lt;/p&gt;

&lt;p&gt;Wasm allows you to compile programming languages that normally compile to
native code, like C, C++, and Rust, to a portable binary that the browser can
execute. This is better than compiling (transpiling) them to Javascript,
because it doesn’t require the overhead of guaranteeing Javascript behavior
while actually executing another language’s compiled code.&lt;/p&gt;

&lt;p&gt;Great. I’m not a fan of Javascript, so this should mean I’ll be able to do web
coding without writing even a single line of Javascript. Well, aside from the
line that goes “load the Wasm”.&lt;/p&gt;

&lt;p&gt;More importantly for this project, it means I’ll be able to run RustRadio in
the browser.&lt;/p&gt;

&lt;h2 id=&quot;web-workers&quot;&gt;Web workers&lt;/h2&gt;

&lt;p&gt;Javascript is single threaded. That’s not a problem in itself, since RustRadio
is perfectly happy running single threaded. But if something get stuck in a
loop then you’ll get the “Page is not responding” dialog, making you and your
users unhappy. And even without that, while some heavy computation is happening
in the main UI thread, the page will be unresponsive.&lt;/p&gt;

&lt;p&gt;Javascript will likely never have threads in the traditional sense (some
frameworks fake it, as I’ll get to).&lt;/p&gt;

&lt;p&gt;So if you want to avoid locking up the main UI thread, then you need to spin
off a “Worker”. This worker runs in its own thread, and has very limited access
to the outside world, including other workers. In particular, it does not have
access to the DOM (the web page). Only the main UI “thread” has access to that.&lt;/p&gt;

&lt;p&gt;What you can do is post messages between the worker and the other worker or
main thread that spawned it. You can also create a shared buffer, but I’ve not
done that yet, so ignoring that for now.&lt;/p&gt;

&lt;p&gt;Any heavy CPU work that’s expected to take a few milliseconds should be
considered for offload to a Worker instead of running on the main UI thread.
And keep in mind low end devices like phones, when estimating “a few
milliseconds”.&lt;/p&gt;

&lt;p&gt;The sandboxing of a Worker is also nice, in that if something runs in a worker,
then you know that it can’t affect the DOM.&lt;/p&gt;

&lt;p&gt;It’s apparently possible to run threads with Wasm in some setups, but it’s not
been standardized, so it’s not something I’m going to build for. In particular,
it doesn’t work on Apple phones, which is a big enough target that it’s a
showstopper.&lt;/p&gt;

&lt;p&gt;So for my purposes the only way to run a separate thread is to run a separate
worker.&lt;/p&gt;

&lt;h3 id=&quot;computing-and-receiving-messages-on-the-same-thread&quot;&gt;Computing and receiving messages on the same thread&lt;/h3&gt;

&lt;p&gt;A worker receives a message by having its &lt;code class=&quot;highlighter-rouge&quot;&gt;onmessage&lt;/code&gt; handler called. But since
the worker is just the one thread, it can’t interrupt whatever heavy
computation was currently happening. It can’t even do something POSIX signal
like, and redirect execution into a new stack. No, it just patiently waits
until the worker finishes what it’s doing before calling that callback.&lt;/p&gt;

&lt;p&gt;There are two ways of doing this. Either the worker snapshots its computation,
and hopes it’ll get a message later, resuming it, or (technically equivalently)
you use &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; functions.  The latter seems better in my limited experience.
Because &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; is just fancy syntax for returning a future, it’s not actually
different from returning and resuming; It’s just automatic. It also means that
having returned, any other &lt;code class=&quot;highlighter-rouge&quot;&gt;onmessage&lt;/code&gt; handler for more messages have a chance
to run.&lt;/p&gt;

&lt;h3 id=&quot;async-sync-async&quot;&gt;async-&amp;gt;sync-&amp;gt;async&lt;/h3&gt;

&lt;p&gt;Sometimes async code needs to send a sync function to some API (e.g. register a
sync callback).  This handler is then called outside of async, and can’t
&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; on any async. &lt;code class=&quot;highlighter-rouge&quot;&gt;onmessage&lt;/code&gt; and button click handlers are sync, yet may
need to &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;. This is part of that whole &lt;a href=&quot;https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/&quot;&gt;“what color is your
function”&lt;/a&gt; thing.&lt;/p&gt;

&lt;p&gt;It seems that the core library of Rust Wasm, &lt;code class=&quot;highlighter-rouge&quot;&gt;wasm_bindgen&lt;/code&gt;, helpfully provides
a function to register more futures for execution, with &lt;code class=&quot;highlighter-rouge&quot;&gt;spawn_local()&lt;/code&gt;. This
makes things easy. The handler then just registers the async call, and returns.&lt;/p&gt;

&lt;h2 id=&quot;my-example-an-ax25-1200bps-decoder-in-the-browser&quot;&gt;My example: An AX.25 1200bps decoder, in the browser&lt;/h2&gt;

&lt;p&gt;I’ve blogged about &lt;a href=&quot;https://en.wikipedia.org/wiki/AX.25&quot;&gt;AX.25&lt;/a&gt; before. For this post all you need to know
is that it’s a data packet sent over radio, and that my example is able to find
and decode the packet from a recording.&lt;/p&gt;

&lt;p&gt;The live site is &lt;a href=&quot;https://thomashabets.github.io/ruwasm/&quot;&gt;here&lt;/a&gt; if you want to try it. Or just enjoy this
screenshot and video if you don’t. :-)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2026-04-ruwasm.png&quot;&gt;&lt;img src=&quot;/static/2026-04-ruwasm.png&quot; alt=&quot;ruwasm&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;iframe width=&quot;788&quot; height=&quot;503&quot; src=&quot;https://www.youtube.com/embed/vv0oimMiLGM&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;my-first-memory-error-in-rust&quot;&gt;My first memory error in Rust&lt;/h2&gt;

&lt;p&gt;Rust is a memory safe language. JS is too. So how is &lt;em&gt;this&lt;/em&gt; the first time I’ve
managed to get memory corruption with Rust?&lt;/p&gt;

&lt;p&gt;Notice in this screenshot how the vector has been corrupted, thinking it’s over
a GB of data. It should be 64Ki.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2026-04-error.png&quot;&gt;&lt;img src=&quot;/static/2026-04-error.png&quot; alt=&quot;Vector size corruption and drain error&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully I’ll get this fixed, so for future reference this is when running the
code in the &lt;a href=&quot;https://github.com/ThomasHabets/ruwasm&quot;&gt;ruwasm&lt;/a&gt; repo at commit
6088f711141fe566a8a02f9f40d5866ff6d8e82f.&lt;/p&gt;

&lt;p&gt;Maybe it’s from some immature dependency. Maybe (more likely?) it’s a bug in my
sloppily coded buffer handler. It does have a couple of &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;github-pages-autodeploy&quot;&gt;github pages autodeploy&lt;/h2&gt;

&lt;p&gt;You may have noticed that the URL to the live demo is hosted on github.io.&lt;/p&gt;

&lt;p&gt;Because there’s no active server component, I thought I’d try out continuous
deployment of this Wasm application. It was simpler than I expected. Just
enable github pages, set them to be sourced from github actions, and add &lt;a href=&quot;https://github.com/ThomasHabets/ruwasm/blob/main/.github/workflows/deploy.yml&quot;&gt;a
simple github action&lt;/a&gt;, and upon every &lt;code class=&quot;highlighter-rouge&quot;&gt;git push&lt;/code&gt;, the latest version
gets hosted “in the cloud”.&lt;/p&gt;

&lt;p&gt;That’s it. Now whenever I push a new version to github, it’s live on that URL a
minute later. Pretty cool.&lt;/p&gt;

&lt;p&gt;Future work is to trigger another github action on tagging a release, creating
a permanent location where this tagged version is deployed. That way it’ll be
possible to bisect looking for a bug, without even compiling.&lt;/p&gt;

&lt;p&gt;I’ll also want to have it trigger on pull requests, so that it can be tested
prior to merge, without the need to download and build locally.&lt;/p&gt;

&lt;h2 id=&quot;wasm-the-end-of-javascript&quot;&gt;Wasm, the end of Javascript?&lt;/h2&gt;

&lt;p&gt;Of course this is &lt;a href=&quot;https://en.wikipedia.org/wiki/Betteridge's_law_of_headlines&quot;&gt;Betteridge’s law of headlines&lt;/a&gt;, but &lt;a href=&quot;https://youtu.be/ceH0IT-OBCw?list=PLNVwswC38Mi3MaVkLWlql31s3j5_cDAsZ&quot;&gt;this
video&lt;/a&gt; is still interesting and relevant.&lt;/p&gt;

&lt;p&gt;WebUSB should provide direct access to SDR hardware like RTLSDR and USRPs.
There’s no reason it shouldn’t be possible to run a large complex interactive
SDR application directly against hardware, all inside the browser with no
server components.&lt;/p&gt;

</description>
        <pubDate>Sat, 11 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/04/Rustradio-SDR-framework-now-also-in-the-browser-with-wasm.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/04/Rustradio-SDR-framework-now-also-in-the-browser-with-wasm.html</guid>
        
        
        <category>wasm</category>
        
        <category>sdr</category>
        
        <category>radio</category>
        
        <category>rust</category>
        
        <category>rustradio</category>
        
      </item>
    
      <item>
        <title>The strange webserver hot potato — sending file descriptors</title>
        <description>&lt;p&gt;I’ve &lt;a href=&quot;/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html&quot;&gt;previously mentioned my io-uring webserver tarweb&lt;/a&gt;. I’ve now
added another interesting aspect to it.&lt;/p&gt;

&lt;p&gt;As you may or may not be aware, on Linux it’s possible to send a file
descriptor from one process to another over a unix domain socket. That’s
actually pretty magic if you think about it.&lt;/p&gt;

&lt;p&gt;You can also send unix credentials and SELinux security contexts, but that’s a
story for another day.&lt;/p&gt;

&lt;h2 id=&quot;my-goal&quot;&gt;My goal&lt;/h2&gt;

&lt;p&gt;I want to run some domains using my webserver “tarweb”. But not all. And I want
to host them on a single IP address, on the normal HTTPS port 443.&lt;/p&gt;

&lt;p&gt;Simple, right? Just use nginx’s &lt;code class=&quot;highlighter-rouge&quot;&gt;proxy_pass&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Ah, but I don’t want nginx to &lt;em&gt;stay&lt;/em&gt; in the path. After &lt;a href=&quot;https://en.wikipedia.org/wiki/Server_Name_Indication&quot;&gt;SNI&lt;/a&gt; (read:
“browser saying which domain it wants”) has been identified I want the TCP
connection to go &lt;em&gt;directly&lt;/em&gt; from the browser to the correct backend.&lt;/p&gt;

&lt;p&gt;I’m sure somewhere on the internet there’s already an SNI router that does
this, but all the ones I found stay in line with the request path, adding a
hop.&lt;/p&gt;

&lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;

&lt;p&gt;A few reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Having all bytes bounce on the SNI router triples the number of total file
descriptors for the connection. (one on the backend, then one each on the
router for upstream and downstream). There are limits per process and system
wide, and the more you have the more you need to juggle them in code.&lt;/li&gt;
  &lt;li&gt;It also wastes CPU and RAM.&lt;/li&gt;
  &lt;li&gt;I want the backend to know the real client IP address, via &lt;code class=&quot;highlighter-rouge&quot;&gt;getpeername()&lt;/code&gt;
or similar, on the socket itself.&lt;/li&gt;
  &lt;li&gt;I don’t want restarting nginx to cut existing connections to backends.&lt;/li&gt;
  &lt;li&gt;I’d like to use TLS keys that the nginx user doesn’t have access to.&lt;/li&gt;
  &lt;li&gt;I used &lt;code class=&quot;highlighter-rouge&quot;&gt;proxy_pass&lt;/code&gt; for &lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;livecount&lt;/a&gt;, and last time I got blog
posts on &lt;a href=&quot;/static/2025-09-top-two.png&quot;&gt;HackerNews&lt;/a&gt; nginx ran out of file descriptors, and started
serving 500 for it serving just plain old static files on disk. For now
I’ve moved livecount to a different port, but in the long run I want it back
on port 443, and yet isolated from nginx so that the latter keeps working
even if livecount is overloaded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Livecount has an open websocket to every open browser tab in the world reading
a given page, so they add up. (no, it doesn’t log. It just keeps count)&lt;/p&gt;

&lt;h2 id=&quot;what-i-built&quot;&gt;What I built&lt;/h2&gt;

&lt;p&gt;I built &lt;a href=&quot;https://github.com/ThomasHabets/tarweb/blob/main/src/sni/sni.rs&quot;&gt;a proof of concept SNI router&lt;/a&gt;. It is a frontline server
receiving TCP connections, on which it then snoops the SNI from the TLS
ClientHello, and routes the connection according to its given rules.&lt;/p&gt;

&lt;p&gt;Anything it reads from the socket is sent along to the real backend along with
the file descriptor. So the backend (in my use that’s tarweb) needs to have
code cooperating to receive the new connection.&lt;/p&gt;

&lt;p&gt;It’s not the cleanest code, but it works. I got ChatGPT to write the boring
“parse the TLS record / ClientHello” parts. Rust is a memory safe language, so
“how bad could it be?”. :-)&lt;/p&gt;

&lt;p&gt;It seems to work for all the currently used TLS versions.&lt;/p&gt;

&lt;h2 id=&quot;its-not-plug-and-play&quot;&gt;It’s not plug and play&lt;/h2&gt;

&lt;p&gt;As I said, it requires the backend to be ready to receive “hey, here’s a file
descriptor, and here’s the first few hundred bytes you should treat as if
you’ve read them from the client”.&lt;/p&gt;

&lt;p&gt;File descriptors don’t have an operation to “unread”. If they did then this
would be easier.  Then it would “just” be a matter of giving a backend
webserver a file descriptor. For some use cases that could mean starting a new
webserver process that reads and writes from stdin/stdout.&lt;/p&gt;

&lt;p&gt;Not super efficient to go back to the fork-exec-per-connection model from the
previous century, but it would achieve the direct connection.&lt;/p&gt;

&lt;p&gt;But the details are academic. We &lt;em&gt;do&lt;/em&gt; need to pass along the snooped bytes
somehow, or the TLS handshake won’t succeed. Which means it does need
cooperation from the backend.&lt;/p&gt;

&lt;h2 id=&quot;but-it-is-privacy-preserving&quot;&gt;But it is privacy preserving&lt;/h2&gt;

&lt;p&gt;Because the SNI router never writes to the client, and therefore doesn’t
perform a TLS handshake, it doesn’t need any private keys or certificates.&lt;/p&gt;

&lt;p&gt;The SNI router has no secrets, and sees no secrets.&lt;/p&gt;

&lt;p&gt;I also added a mode that proxies the TCP connection, if some SNI should be
routed to a different server. But of course then it’s not possible to pass the
file descriptor. So encrypted bytes will bounce on the SNI router for that kind
of flow. But still the SNI router is not able to decrypt anything.&lt;/p&gt;

&lt;p&gt;A downside is of course that bouncing the connection around the world will slow
it down, add latency, and waste resources. So pass the file descriptor where
possible.&lt;/p&gt;

&lt;h2 id=&quot;the-hot-potato&quot;&gt;The hot potato&lt;/h2&gt;

&lt;p&gt;So now my setup has the SNI router accept the connection, and then throw the
very file descriptor over to tarweb, saying “you deal with this TCP
connection”. Tarweb does the TLS handshake, and then throws the TLS session
keys over to the kernel, saying “I can’t be bothered doing encryption, you do
it”, and then actually handles the HTTP requests.&lt;/p&gt;

&lt;p&gt;Well actually, there’s another strange indirection. When tarweb receives a file
descriptor, it uses io-uring &lt;a href=&quot;https://man7.org/linux/man-pages/man2/io_uring_register.2.html&quot;&gt;“registered files”&lt;/a&gt; to turn it into a
“fixed file handle”, and closes the original file descriptor. On the kernel
side there’s still a file descriptor of course, but there’s nothing in
&lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/&amp;lt;pid&amp;gt;/fd/&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ls /proc/699874/fd -l
total 0
lrwx------ 1 thomas thomas 64 Oct 26 21:47 0 -&amp;gt; /dev/pts/5
lrwx------ 1 thomas thomas 64 Oct 26 21:47 1 -&amp;gt; /dev/pts/5
lrwx------ 1 thomas thomas 64 Oct 26 21:47 2 -&amp;gt; /dev/pts/5
lrwx------ 1 thomas thomas 64 Oct 26 21:47 3 -&amp;gt; anon_inode:[io_uring]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This improves performance a bit on the linux kernel side.&lt;/p&gt;

&lt;p&gt;The SNI router does not use io-uring. At least not yet. The SNI router’s job is
much smaller (doesn’t even do a TLS handshake), much more brief (it almost
immediately passes the file descriptor to tarweb), and much less concurrency
(because of the connections being so short lived as far as it’s concerned),
that it may not be worth it.&lt;/p&gt;

&lt;p&gt;In normal use the SNI router only needs these syscalls per connection:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;accept()&lt;/code&gt; for the new connection,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;read()&lt;/code&gt; a few hundred bytes of ClientHello,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sendmsg()&lt;/code&gt; of same size to pass it on,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;close()&lt;/code&gt; to forget the file descriptor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;http3-can-redirect-connections&quot;&gt;HTTP/3 can redirect connections&lt;/h2&gt;

&lt;p&gt;At the risk of going off on an unrelated tangent, HTTP/3 (QUIC-based) has &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9000#section-9.6&quot;&gt;an
interesting way of telling a client to “go over there”&lt;/a&gt;. A built in load
balancer inside the protocol, you could say, sparing the load balancer needing
to proxy everything.&lt;/p&gt;

&lt;p&gt;This opens up opportunities to steer not just on SNI, and is much more flexible
than DNS, all without needing the “proxy” to be inline.&lt;/p&gt;

&lt;p&gt;E.g. say a browser is in Sweden, and you have servers in Norway and Italy. And
say you have measured, and find that it would be best if the browser connected
to your Norway server. But due to peering agreements and other fun stuff, Italy
will be preferred on any BGP anycasted address.&lt;/p&gt;

&lt;p&gt;You then have a few possible options, and I do mean they’re all possible:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Have the browser connect to &lt;code class=&quot;highlighter-rouge&quot;&gt;norway.example.com&lt;/code&gt;, with Norway-specific
IP addresses. Not great. People will start bookmarking these URLs, and what
happens when you move your Norway servers to Denmark? &lt;code class=&quot;highlighter-rouge&quot;&gt;norway.example.com&lt;/code&gt;
now goes to servers in Denmark?&lt;/li&gt;
  &lt;li&gt;Use DNS based load balancing, giving Swedish browsers the Norway unicast
IPs. Yes… but this is WAY more work than you probably think. And WAY less
reliable at giving the best experience for the long tail. And sometimes
your most important customer is in that long tail.&lt;/li&gt;
  &lt;li&gt;Try to traffic engineer the whole Internet with BGP announcement tweaks.
Good luck with that, for the medium to long tail.&lt;/li&gt;
  &lt;li&gt;Install servers in Sweden, and any other place you may have users. Then you
can anycast your addresses from there, and have full control of how you
proxy (or packet by packet traffic engineer over tunnels) them. Expensive if
you have many locations you need to do this in. Some traffic will still go
to the wrong anycast entry point, but pretty feasible though expensive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The two DNS-based ones also have the valid concern that screwing up DNS can
have &lt;a href=&quot;https://aws.amazon.com/message/101925/&quot;&gt;bad consequences&lt;/a&gt;. If you can leave DNS alone that’s better.&lt;/p&gt;

&lt;p&gt;Back to HTTP/3. If you’ve set up HTTP/3 it may be because you care about
latency. It’s then easier to act on information you have about every single
connection. On an individual connection basis you can tell the browser in
Sweden that it should now talk to the servers in Norway. All without DNS or
anycast.&lt;/p&gt;

&lt;p&gt;Which is nice, because running a webserver is hard enough. Also running a
dynamic DNS service or anycast has even more opportunities to blow up
fantastically.&lt;/p&gt;

&lt;h2 id=&quot;where-was-i-oh-yeah-file-descriptors&quot;&gt;Where was I? Oh yeah, file descriptors&lt;/h2&gt;

&lt;p&gt;I should add that HTTP/3 doesn’t have the “running out of file descriptors”
problem. Being based on UDP you can run your entire service with just a single
file descriptor. Connections are identified by IDs, not 5-tuples.&lt;/p&gt;

&lt;p&gt;So why didn’t I just use HTTP/3?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;HTTP/3 is complex. You can build &lt;a href=&quot;/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html&quot;&gt;a weird io-uring kTLS based
webserver&lt;/a&gt; on a weekend, and control everything (except TLS
handshakes). Implementing HTTP/3 from scratch, and controlling everything,
is a different beast.&lt;/li&gt;
  &lt;li&gt;HTTP/1 needs to still work. Not all clients support HTTP/3, and HTTP/1 or 2
is even used to bootstrap HTTP/3 via its &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt-Svc&lt;/code&gt; header.&lt;/li&gt;
  &lt;li&gt;Preferred address in HTTP/3 is just a suggestion. Browsers don’t have to
actually move.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;what-about-encrypted-sni-esni-or-encrypted-clienthello-ech&quot;&gt;What about encrypted SNI (ESNI), or encrypted ClientHello (ECH)&lt;/h2&gt;

&lt;p&gt;No support for that (yet). From some skimming &lt;a href=&quot;https://www.cloudflare.com/en-gb/learning/ssl/what-is-encrypted-sni/&quot;&gt;ESNI&lt;/a&gt; should “just work”,
with just a minor decryption operation in the SNI router.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.cloudflare.com/encrypted-client-hello/&quot;&gt;ECH&lt;/a&gt; seems harder. It should still be doable, but the SNI router will
need to do the full handshake, or close to it. And after taking its routing
decision it needs to transfer the encryption state to the backend, along with
the file descriptor.&lt;/p&gt;

&lt;p&gt;This is not impossible, of course. It’s similar to how tarweb passes the TLS
session keys to the kernel. But it likely does mean that the SNI router needs
to have access to both the TLS session keys and maybe even the domain TLS
private keys.&lt;/p&gt;

&lt;p&gt;But that’s a problem for another day.&lt;/p&gt;

&lt;h2 id=&quot;related-previous-posts&quot;&gt;Related previous posts&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/11/Fast-zero-copy-static-web-server-with-KTLS.html&quot;&gt;tarweb was first written in C++&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;livecount keeps long lived connection&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html&quot;&gt;tarweb rewritten in Rust, and using io-uring&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2010/05/Redirecting-to-the-closest-site-using-Javascript.html&quot;&gt;You can redirect with Javascript&lt;/a&gt;, but this still has the
&lt;code class=&quot;highlighter-rouge&quot;&gt;norway.example.com&lt;/code&gt; problem.&lt;/li&gt;
  &lt;li&gt;I passed file descriptors between processes &lt;a href=&quot;/2009/03/Moving-a-process-to-another-terminal.html&quot;&gt;in injcode&lt;/a&gt;, but it was
only ever a proof of concept that only worked on 32bit x86, and the code
doesn’t look like it actually does it? Anyway I can’t expect to remember code
from 17 years ago.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sun, 26 Oct 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/10/The-strange-webserver-hot-potato-sending-file-descriptors.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/10/The-strange-webserver-hot-potato-sending-file-descriptors.html</guid>
        
        
        <category>programming</category>
        
        <category>unix</category>
        
        <category>network</category>
        
      </item>
    
      <item>
        <title>Ideal programming language</title>
        <description>&lt;p&gt;My &lt;a href=&quot;/2025/07/Go-is-still-not-good.html&quot;&gt;last post about Go&lt;/a&gt; got &lt;a href=&quot;https://news.ycombinator.com/item?id=44982491&quot;&gt;some
attention&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In fact, &lt;a href=&quot;/static/2025-09-top-two.png&quot;&gt;two of my posts got attention that day&lt;/a&gt;, which broke my nginx
since I was running &lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;livecount&lt;/a&gt; behind nginx, making me run out of
file descriptors when thousands of people had the page opened.&lt;/p&gt;

&lt;p&gt;It’s a shame that I had to turn off livecount, since it’d be cool to see the
stats. But I was out of the country, with unreliable access to both Internet
and even electricity in hotels, so I couldn’t implement the real fix until I
got back, when it had already mostly died down.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-09-traffic.png&quot;&gt;&lt;img src=&quot;/static/2025-09-traffic.png&quot; alt=&quot;traffic&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I knew this was a problem with livecount, of course, and I even allude to it in
&lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;its blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyway, back to programming languages.&lt;/p&gt;

&lt;p&gt;The reactions to my post can be summarized as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Oh yes, these things are definite flaws in the language.&lt;/li&gt;
  &lt;li&gt;What you’re saying is true, but it’s not a problem. Your post is pointless.&lt;/li&gt;
  &lt;li&gt;You’re dumb. You don’t understand Go. Here let me explain your own blog post
to you […]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I respect the first two. The last one has to be from people who are too
emotionally invested with their tools, and take articles like this like a
personal attack of some sort. They go out of their way to be offended, and then
start screaming &lt;a href=&quot;https://youtu.be/L3dxMGzt5mU&quot;&gt;“but I don’t fucking want guitar lessons!”&lt;/a&gt;. They want
to counter attack against another programming language, thinking I would take
it personally too. Maybe this heretic is a Java programmer, and that’s why he’s
stupid? (&lt;a href=&quot;/2022/08/Java-a-fractal-of-bad-experiments.html&quot;&gt;bad guess&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It also reminded me of PHP programmers back in the PHP 3.x days who would die
on the hill of defending PHP as an awesome language, while admitting that they
knew literally no other language. &lt;a href=&quot;https://www.kiplingsociety.co.uk/poem/poems_englishflag.htm&quot;&gt;What should they know of England who only
England know?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m not offended. Those replies are not offensive; They’re boring. There’s
nothing to learn from the comments, and probably not from the people making
such comments in general, either.&lt;/p&gt;

&lt;p&gt;Also it seems that somebody managed to get my whole Blog comment database
deleted from disqus. Either disqus itself was hacked, or just my account with
them. Or someone tricked disqus into deleting it. They managed to restore it,
though.&lt;/p&gt;

&lt;p&gt;Keeping this third type of commenter in mind, I got an email a few days later
asking what programming languages are “closer to ideal”, and if I maybe have a
blog post in me about that.&lt;/p&gt;

&lt;p&gt;I don’t know who’s asking, so I replied the long version, while still taking
the question at face value.&lt;/p&gt;

&lt;h2 id=&quot;my-reply-after-minor-edits&quot;&gt;My reply (after minor edits)&lt;/h2&gt;

&lt;p&gt;Ideal… Well, this is getting into the space of “what is the best
programming language”, which doesn’t have a perfect answer. To do
what?&lt;/p&gt;

&lt;p&gt;To make an Android app (something I’m not an expert in. I’ve just made
one simple one), I think Kotlin seems nice. But I don’t know it very
well.&lt;/p&gt;

&lt;p&gt;For web development, something I also don’t do much, it’s probably
Typescript.&lt;/p&gt;

&lt;p&gt;For maximum portability for systems programming, C or C++ (depending
on if all your target platforms (e.g. embedded stuff) support C++) is
probably best.&lt;/p&gt;

&lt;p&gt;But these are practical answers. Some people like Lisp. Others like Haskell.
Rust strikes a good position between practical, low level control, safe, and a
high level type system. If the Rust compiler supports your platform, then it’s
pretty much as portable as C/C++.&lt;/p&gt;

&lt;p&gt;I’ve written about the deficiencies of &lt;a href=&quot;/2022/08/Java-a-fractal-of-bad-experiments.html&quot;&gt;Java&lt;/a&gt; and &lt;a href=&quot;/2025/07/Go-is-still-not-good.html&quot;&gt;Go&lt;/a&gt; because the
ways they are deficient are interesting. I don’t find the ways C++ is deficient
to be interesting. C++ is what it is. I happen to enjoy coding C++.&lt;/p&gt;

&lt;p&gt;I also have thoughts about the trap of &lt;a href=&quot;/2021/06/The-uselessness-of-bash.html&quot;&gt;accidentally writing too much in
bash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have a draft of things wrong with Rust. But so far I think they are
all fixable. (e.g. no placement syntax has been defined &lt;em&gt;yet&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;But I have no interest in writing a blog post about the lack of memory
safety of C++. It is what it is.&lt;/p&gt;

&lt;p&gt;Java’s deficiencies are interesting because they are the best guesses
of the future that 1990 had to offer. And those guesses were almost
all wrong.&lt;/p&gt;

&lt;p&gt;Go’s deficiencies are interesting/frustrating because it (almost entirely) is
the best that the 1980s-early 1990s had to offer. And yet it launched in 2009.&lt;/p&gt;

&lt;h2 id=&quot;enjoyment&quot;&gt;Enjoyment&lt;/h2&gt;

&lt;p&gt;I don’t enjoy coding Javascript. So I’m experimenting writing frontend stuff in
Rust and compile to WASM. So far it works for me, but is not something I’d
recommend for anyone who wants to get anything done.&lt;/p&gt;

&lt;p&gt;But no, I won’t be writing up which programming language is “ideal”,
because it’s one of those “it depends”.&lt;/p&gt;

</description>
        <pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/09/Ideal-programming-languages.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/09/Ideal-programming-languages.html</guid>
        
        
        <category>programming</category>
        
      </item>
    
      <item>
        <title>Go is still not good</title>
        <description>&lt;p&gt;Previous posts &lt;a href=&quot;/2013/10/Why-Go-is-not-my-favourite-language.html&quot;&gt;Why Go is not my favourite language&lt;/a&gt; and &lt;a href=&quot;/2022/02/Go-programs-are-not-portable.html&quot;&gt;Go programs
are not portable&lt;/a&gt; have me critiquing Go for over a decade.&lt;/p&gt;

&lt;p&gt;These things about Go are bugging me more and more. Mostly because they’re so
unnecessary. The world knew better, and yet Go was created the way it was.&lt;/p&gt;

&lt;p&gt;For readers of previous posts you’ll find some things repeated here. Sorry
about that.&lt;/p&gt;

&lt;h2 id=&quot;error-variable-scope-is-forced-to-be-wrong&quot;&gt;Error variable scope is forced to be wrong&lt;/h2&gt;

&lt;p&gt;Here’s an example of the language forcing you to do the wrong thing. It’s very
helpful for the reader of code (and code is read more often than it’s written),
to minimize the scope of a variable. If by mere syntax you can tell the reader
that a variable is just used in these two lines, then that’s a good thing.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;(enough has been said about this verbose repeated boilerplate that I don’t have
to. I also don’t particularly care)&lt;/p&gt;

&lt;p&gt;So that’s fine. The reader knows &lt;code class=&quot;highlighter-rouge&quot;&gt;err&lt;/code&gt; is here and only here.&lt;/p&gt;

&lt;p&gt;But then you encounter this:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lot&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;below&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Wait, what? Why is &lt;code class=&quot;highlighter-rouge&quot;&gt;err&lt;/code&gt; reused for &lt;code class=&quot;highlighter-rouge&quot;&gt;foo2()&lt;/code&gt;? Is there’s something subtle I’m
not seeing? Even if we change that to &lt;code class=&quot;highlighter-rouge&quot;&gt;:=&lt;/code&gt;, we’re left to wonder why &lt;code class=&quot;highlighter-rouge&quot;&gt;err&lt;/code&gt; is
in scope for (potentially) the rest of the function. Why? Is it read later?&lt;/p&gt;

&lt;p&gt;Especially when looking for bugs, an experienced coder will see these things
and slow down, because here be dragons. Ok, now I’ve wasted a couple of seconds
on the red herring of reusing &lt;code class=&quot;highlighter-rouge&quot;&gt;err&lt;/code&gt; for &lt;code class=&quot;highlighter-rouge&quot;&gt;foo2()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Is a bug perhaps that the function ends with this?&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// Return foo99() error. (oops, that's not what we're doing)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo99&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// This is `err` from way up there in the foo() call.&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Why does the scope of &lt;code class=&quot;highlighter-rouge&quot;&gt;err&lt;/code&gt; extend way beyond where it’s relevant?&lt;/p&gt;

&lt;p&gt;The code would have been so much easier to read if only &lt;code class=&quot;highlighter-rouge&quot;&gt;err&lt;/code&gt;’s scope had been
smaller. But that’s not syntactically possible in Go.&lt;/p&gt;

&lt;p&gt;This was not thought through. Deciding on this was not thinking, it was typing.&lt;/p&gt;

&lt;h2 id=&quot;two-types-of-nil&quot;&gt;Two types of nil&lt;/h2&gt;

&lt;p&gt;Look at this nonsense:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// nil nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// t,t,f: They're equal, but they're not.&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// nil nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// t,f,t: They are not equal, but they are.&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Go was not satisfied with one &lt;a href=&quot;https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/&quot;&gt;billion dollar
mistake&lt;/a&gt;,
so they decided to have &lt;strong&gt;two&lt;/strong&gt; flavors of &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;“What color is your nil?” — The two billion dollar mistake.&lt;/p&gt;

&lt;p&gt;The reason for the difference boils down to again, not thinking, just typing.&lt;/p&gt;

&lt;h2 id=&quot;its-not-portable&quot;&gt;It’s not portable&lt;/h2&gt;

&lt;p&gt;Adding comment near the top of the file for conditional compilation must be the
dumbest thing ever. Anybody who’s actually tried to maintain a portable program
will tell you this will only cause suffering.&lt;/p&gt;

&lt;p&gt;It’s an &lt;a href=&quot;https://en.wikipedia.org/wiki/Aristotelian_physics&quot;&gt;Aristotle way of the science&lt;/a&gt; of designing a language; lock
yourself up in a room, and never test your hypotheses against reality.&lt;/p&gt;

&lt;p&gt;The problem is that this is not year 350 BCE. We actually have experience that
aside from air resistance, heavy and light objects actually fall at the same
speed. And we have experience with portable programs, and would not do
something this dumb.&lt;/p&gt;

&lt;p&gt;If this had been the year 350 BCE, then this could be forgiven. Science as we
know it hadn’t been invented yet. But this is after decades of very widely
available experience in portability.&lt;/p&gt;

&lt;p&gt;More details in &lt;a href=&quot;/2022/02/Go-programs-are-not-portable.html&quot;&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;append-with-no-defined-ownership&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;append&lt;/code&gt; with no defined ownership&lt;/h2&gt;

&lt;p&gt;What does this print?&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NIGHTMARE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Probably &lt;code class=&quot;highlighter-rouge&quot;&gt;[hello NIGHTMARE !]&lt;/code&gt;. Who wants that? Nobody wants that.&lt;/p&gt;

&lt;p&gt;Ok, how about this?&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BACON&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;THIS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SHOULD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WORK&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;If you guessed &lt;code class=&quot;highlighter-rouge&quot;&gt;[hello world !]&lt;/code&gt;, then you know more than anybody should have
to know about quirks of a stupid programming language.&lt;/p&gt;

&lt;h2 id=&quot;defer-is-dumb&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt; is dumb&lt;/h2&gt;

&lt;p&gt;Even in a GC language, sometimes you just can’t wait to destroy a resource. It
really does need to run as we leave the local code, be it by normal return, or
via an exception (aka panic).&lt;/p&gt;

&lt;p&gt;What we clearly want is RAII, or something like it.&lt;/p&gt;

&lt;p&gt;Java has it:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyResource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyResource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;cm&quot;&gt;/*
  work with resource r, which will be cleaned up when the scope ends via
  .close(), not merely when the GC feels like it.
  */&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Python has it. Though Python is &lt;em&gt;almost&lt;/em&gt; entirely refcounted, so one can pretty
much rely on the &lt;code class=&quot;highlighter-rouge&quot;&gt;__del__&lt;/code&gt; finalizer being called. But if it’s important, then
there’s the &lt;code class=&quot;highlighter-rouge&quot;&gt;with&lt;/code&gt; syntax.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# some code. At end of the block __exit__ will be called on res.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Go? Go makes you go read the manual and see if this particular resource needs
to have a defer function called on it, and which one.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This is so dumb. Some resources need a defer destroy. Some don’t. Which ones?
Good fucking luck.&lt;/p&gt;

&lt;p&gt;And you also regularly end up with stuff like this monstrosity:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;openFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Yes, this is what you NEED to do to safely write something to a file in Go.&lt;/p&gt;

&lt;p&gt;What’s this, a &lt;em&gt;second&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Close()&lt;/code&gt;? Oh yeah, of course that’s needed. Is it even
safe to double-close, or does my defer need to check for that? It happens to be
safe on &lt;code class=&quot;highlighter-rouge&quot;&gt;os.File&lt;/code&gt;, but on other things: WHO KNOWS?!&lt;/p&gt;

&lt;h2 id=&quot;the-standard-library-swallows-exceptions-so-all-hope-is-lost&quot;&gt;The standard library swallows exceptions, so all hope is lost&lt;/h2&gt;

&lt;p&gt;(Largely a repeat of part of &lt;a href=&quot;/2013/10/Why-Go-is-not-my-favourite-language.html&quot;&gt;a previous post&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Go says it doesn’t have exceptions. Go makes it extremely awkward to use
exceptions, because they want to punish programmers who use them.&lt;/p&gt;

&lt;p&gt;Ok, fine so far.&lt;/p&gt;

&lt;p&gt;But all Go programmers must still write exception safe code. Because while
&lt;em&gt;they&lt;/em&gt; don’t use exceptions, other code will. Things will panic.&lt;/p&gt;

&lt;p&gt;So you need, not should, NEED, to write code like:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;What is this stupid middle endian system? That’s dumb just like putting the day
in the middle of a date is dumb. MMDDYY, honestly? (separate rant)&lt;/p&gt;

&lt;p&gt;But panic will terminate the program, they say, so why do you care if you
unlock a mutex five milliseconds before it exits anyway?&lt;/p&gt;

&lt;p&gt;Because what if something swallows that exception and carries on as normal, and
you’re now stuck with a locked mutex?&lt;/p&gt;

&lt;p&gt;But surely nobody would do that? Reasonable and strict coding standards would
surely prevent it, under penalty of being fired?&lt;/p&gt;

&lt;p&gt;The standard library does that. &lt;code class=&quot;highlighter-rouge&quot;&gt;fmt.Print&lt;/code&gt; when calling &lt;code class=&quot;highlighter-rouge&quot;&gt;.String()&lt;/code&gt;, and the
standard library HTTP server does that, for exceptions in the HTTP handlers.&lt;/p&gt;

&lt;p&gt;All hope is lost. You MUST write exception safe code. But you can’t use
exceptions. You can only have the downsides of exceptions be thrust upon you.&lt;/p&gt;

&lt;p&gt;Don’t let them gaslight you.&lt;/p&gt;

&lt;h2 id=&quot;sometimes-things-arent-utf-8&quot;&gt;Sometimes things aren’t UTF-8&lt;/h2&gt;

&lt;p&gt;If you stuff random binary data into a &lt;code class=&quot;highlighter-rouge&quot;&gt;string&lt;/code&gt;, Go just steams along, as
described &lt;a href=&quot;https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride&quot;&gt;in this post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Over the decades I have lost data to tools skipping non-UTF-8 filenames. I
should not be blamed for having files that were named before UTF-8 existed.&lt;/p&gt;

&lt;p&gt;Well… I had them. They’re gone now. They were silently skipped in a
backup/restore.&lt;/p&gt;

&lt;p&gt;Go wants you to continue losing data. Or at least, when you lose data, it’ll
say “well, what (encoding) was the data wearing?”.&lt;/p&gt;

&lt;p&gt;Or how about you just do something more thought through, when you design a
language? How about doing the right thing, instead of the obviously wrong
simple thing?&lt;/p&gt;

&lt;h2 id=&quot;memory-use&quot;&gt;Memory use&lt;/h2&gt;

&lt;p&gt;Why do I care about memory use? RAM is cheap. Much cheaper than the time it
takes to read this blog post. I care because my service runs on a cloud
instance where you actually pay for RAM. Or you run containers, and you want to
run a thousand of them on the same machine. Your data may &lt;a href=&quot;https://yourdatafitsinram.net/&quot;&gt;fit in
RAM&lt;/a&gt;, but it’s still expensive if you have to
give your thousand containers 4TiB of RAM instead of 1TiB.&lt;/p&gt;

&lt;p&gt;You can manually trigger a GC run with &lt;code class=&quot;highlighter-rouge&quot;&gt;runtime.GC()&lt;/code&gt;, but “oh no don’t do
that”, they say, “it’ll run when it has to, just trust it”.&lt;/p&gt;

&lt;p&gt;Yeah, 90% of the time, that works every time. But then it doesn’t.&lt;/p&gt;

&lt;p&gt;I rewrote some stuff in another language because over time the Go version would
use more and more memory.&lt;/p&gt;

&lt;p&gt;Another example of why memory matters is Android Studio. I basically cannot run
it on my 32GiB laptop. 32GiB is not enough to run an IDE. If I run Android
Studio and Chrome at the same time, then the OOM killer kicks in.&lt;/p&gt;

&lt;h2 id=&quot;it-didnt-have-to-be-this-way&quot;&gt;It didn’t have to be this way&lt;/h2&gt;

&lt;p&gt;We knew better. This was not the COBOL debate over whether to use symbols or
English words.&lt;/p&gt;

&lt;p&gt;And it’s not like when we didn’t know at the time that &lt;a href=&quot;/2022/08/Java-a-fractal-of-bad-experiments.html&quot;&gt;Java’s ideas were
bad&lt;/a&gt;, because we did know Go’s ideas were bad.&lt;/p&gt;

&lt;p&gt;We already knew better than Go, and yet now we’re stuck with bad Go codebases.&lt;/p&gt;

&lt;h2 id=&quot;other-peoples-posts&quot;&gt;Other people’s posts&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.uber.com/en-GB/blog/data-race-patterns-in-go/&quot;&gt;Data race patterns in Go&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fasterthanli.me/articles/lies-we-tell-ourselves-to-keep-using-golang&quot;&gt;Lies we tell ourselves to keep using Golang&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride&quot;&gt;I want off mr golang’s wild ride&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post was &lt;a href=&quot;https://news.ycombinator.com/item?id=44982491&quot;&gt;discussed on HackerNews on
2025-08-22&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Sat, 12 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/07/Go-is-still-not-good.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/07/Go-is-still-not-good.html</guid>
        
        
        <category>rant</category>
        
        <category>programming</category>
        
        <category>golang</category>
        
      </item>
    
      <item>
        <title>Setting clock source with GNU Radio</title>
        <description>&lt;p&gt;I bought &lt;a href=&quot;https://en.wikipedia.org/wiki/GPS_disciplined_oscillator&quot;&gt;a GPS Disciplined Oscillator (GPSDO)&lt;/a&gt;, because I thought it’d
be fun for various projects. Specifically I bought &lt;a href=&quot;https://dxpatrol.pt/produto/dxpatrol-gpsdo-v3/&quot;&gt;this one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started by calibrating my &lt;a href=&quot;https://icomuk.co.uk/IC-9700/Amateur_Radio_Ham_Base_Stations&quot;&gt;ICOM IC-9700&lt;/a&gt;. I made sure it got a GPS
lock, and connected it to the 9700 10MHz reference port, with a 20dB attenuator
inline, just in case. Ok, the receive frequency moved a bit, but how do I know
it was improved? My &lt;a href=&quot;https://kenwoodcommunications.co.uk/amateur-radio/vhf-uhf/handhelds/TH-D75E/?view=details&quot;&gt;D75&lt;/a&gt; was still about 200Hz off frequency.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-d75.png&quot;&gt;&lt;img src=&quot;/static/2025-06-d75.png&quot; alt=&quot;IC9700 screenshot showing D75&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Segal%27s_law&quot;&gt;Segal’s law&lt;/a&gt; paraphrased: “Someone with one radio
knows what frequency they’re on. Someone with two radios is never sure”.&lt;/p&gt;

&lt;p&gt;Unless, of course, that person has two radios with disciplined oscillators.
Which I do. I also have a &lt;a href=&quot;https://www.ettus.com/all-products/ub200-kit/&quot;&gt;USRP B200&lt;/a&gt; with an added &lt;a href=&quot;https://www.ettus.com/all-products/gpsdo-tcxo-module/&quot;&gt;GPSDO
accessory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sidenote: wow, that’s gotten expensive. Today I’d probably use the same GPSDO
from DXPatrol instead (it has four outputs). Note that if you do have the GPSDO
installed in the B200, then you cannot use an external 10MHz reference. It’s a
&lt;a href=&quot;https://files.ettus.com/manual/page_usrp_b200.html#b200_known_issues&quot;&gt;known issue&lt;/a&gt;. Then again if you paid this much, why would you not use
it?&lt;/p&gt;

&lt;h2 id=&quot;configuring-gnu-radio-to-use-the-gpsdo&quot;&gt;Configuring GNU Radio to use the GPSDO&lt;/h2&gt;

&lt;p&gt;First I thought that surely the best reference would be the default, so I
should be able to just send a tone, having configured only frequency and output
gain:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-gr-simple.png&quot;&gt;&lt;img src=&quot;/static/2025-06-gr-simple.png&quot; alt=&quot;Flowgraph showing signal-&amp;gt;sink&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But that doesn’t seem right. It was off by almost a kHz, compared to the 9700!
That’s worse than the D75.&lt;/p&gt;

&lt;p&gt;Let me try with the &lt;code class=&quot;highlighter-rouge&quot;&gt;morse_beacon&lt;/code&gt; example in &lt;a href=&quot;https://github.com/ThomasHabets/rustradio&quot;&gt;RustRadio&lt;/a&gt;, where I
know I set the clock source.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo run -F soapysdr --example morse_beacon -- \
  --sample-rate 320k \
  --freq 436.6m \
  -d 'soapy=0,remote=tcp://my-soapysdr-server,remote:prot=tcp,driver=remote,remote:driver=uhd' \
  --ogain 0.8 \
  --clock-source gpsdo \
  'M0THC'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-morse.png&quot;&gt;&lt;img src=&quot;/static/2025-06-morse.png&quot; alt=&quot;Perfect match with rustradio&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah that’s perfect. So GNU Radio is not setting the clock source, but instead
defaulting to the &lt;code class=&quot;highlighter-rouge&quot;&gt;internal&lt;/code&gt; undisciplined clock source.&lt;/p&gt;

&lt;p&gt;I looked all over the GNU Radio Companion, testing both &lt;code class=&quot;highlighter-rouge&quot;&gt;Soapy Custom Sink&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;Soapy Sink&lt;/code&gt; (deprecated). I added various versions of &lt;code class=&quot;highlighter-rouge&quot;&gt;clock=gpsdo&lt;/code&gt; to driver
strings and parameters. No change.&lt;/p&gt;

&lt;p&gt;Then I went to check the source. The block &lt;em&gt;does&lt;/em&gt; have a &lt;code class=&quot;highlighter-rouge&quot;&gt;clock_source&lt;/code&gt;
parameter, &lt;a href=&quot;https://github.com/gnuradio/gnuradio/blob/ecac63211b43629fe4f492bcd1ea36449177b001/gr-soapy/grc/soapy_sink.block.yml#L45-L48&quot;&gt;but it’s hidden&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Looking further, it’s not only hidden, but also unused. I guess they kept it
only to prevent old flowgraphs from having syntax errors.&lt;/p&gt;

&lt;p&gt;But there is a way to set the clock source. By sending &lt;a href=&quot;https://github.com/gnuradio/gnuradio/blob/ecac63211b43629fe4f492bcd1ea36449177b001/gr-soapy/lib/block_impl.cc#L123&quot;&gt;a message&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I created this flowgraph, with the &lt;code class=&quot;highlighter-rouge&quot;&gt;Message PMT&lt;/code&gt; being
&lt;code class=&quot;highlighter-rouge&quot;&gt;pmt.dict_add(pmt.make_dict(), pmt.intern(&quot;clock_src&quot;), pmt.intern(&quot;gpsdo&quot;))&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-gr-strobe.png&quot;&gt;&lt;img src=&quot;/static/2025-06-gr-strobe.png&quot; alt=&quot;Flowgraph with a message strobe&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It worked. You can clearly see where the strobe message told the Source block
to stop being a silly goose. The internal clock can be seen being almost a kHz
off, and the GPSDO is so perfect you can’t see any error, even with the
waterfall zoomed in as much as it can be.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-strobe.png&quot;&gt;&lt;img src=&quot;/static/2025-06-strobe.png&quot; alt=&quot;Screenshot showing strobe changing the clock&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-icom-ic-9700-can-frequency-lock-now&quot;&gt;The ICOM IC-9700 can frequency lock now?&lt;/h2&gt;

&lt;p&gt;I expected the 9700 to only support a one-off calibration, but looks like it
locks on to the external reference, and keeps tuning forever. And it has a
button saying “Cancel Sync”. It even keeps it enabled across reboots. Nice.&lt;/p&gt;

&lt;p&gt;Does this mean that &lt;a href=&quot;https://www.leobodnar.com/shop/index.php?main_page=product_info&amp;amp;products_id=352&quot;&gt;this injection board&lt;/a&gt; is obsolete?&lt;/p&gt;

&lt;p&gt;My understanding didn’t come out of nothing. &lt;a href=&quot;https://www.ph4x.com/ic-9700-and-10-mhz-gpsdo-lock/&quot;&gt;Here’s a blog post&lt;/a&gt;
that corroborates my memory.&lt;/p&gt;

&lt;p&gt;I guess it was solved in a firmware update? My firmware version is 1.44. Oh I
see, a comment from 2022 says that this has now been fixed. Which… corroborates
what I’m seeing now.&lt;/p&gt;

&lt;p&gt;So yeah. No need for an injection board. Just plug in a 10MHz reference to the
reference port.&lt;/p&gt;

</description>
        <pubDate>Sun, 22 Jun 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/06/Setting-clock-source-with-GNU-Radio.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/06/Setting-clock-source-with-GNU-Radio.html</guid>
        
        
        <category>radio</category>
        
        <category>gnuradio</category>
        
      </item>
    
      <item>
        <title>Software defined KISS modem</title>
        <description>&lt;p&gt;I’ve kept working on my &lt;a href=&quot;https://en.wikipedia.org/wiki/Software-defined_radio&quot;&gt;SDR&lt;/a&gt; framework in Rust called
&lt;a href=&quot;https://github.com/ThomasHabets/rustradio&quot;&gt;RustRadio&lt;/a&gt;, that I’ve blogged about
&lt;a href=&quot;https://blog.habets.se/2023/10/RustRadio-Roast-My-Rust.html&quot;&gt;twice&lt;/a&gt; &lt;a href=&quot;https://blog.habets.se/2023/11/RustRadio-improved-API.html&quot;&gt;before&lt;/a&gt;. I’ve been adding a little bit here, a little
bit there, with one of my goals being to control a whole &lt;a href=&quot;https://en.wikipedia.org/wiki/AX.25&quot;&gt;AX.25&lt;/a&gt; stack.&lt;/p&gt;

&lt;p&gt;As seen in the diagram in &lt;a href=&quot;https://blog.habets.se/2024/09/An-AX.25-implementation-in-Rust.html&quot;&gt;this post&lt;/a&gt;, we need:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Applications, client and server — I’ve made &lt;a href=&quot;https://github.com/ThomasHabets/radiostuff/tree/master/ax25/axsh&quot;&gt;those&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;AX.25 connected mode stack (OSI layer 4, basically) — The kernel’s sucks,
so I &lt;a href=&quot;https://blog.habets.se/2024/09/An-AX.25-implementation-in-Rust.html&quot;&gt;made that too&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;A modem (OSI layer 1-2), turning digital packets into analog radio — The
topic of this post.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-job-of-the-modem&quot;&gt;The job of the modem&lt;/h2&gt;

&lt;p&gt;Applications talk in terms of streams. AX.25 implementation turns that into
individual data frames. The most common protocol for sending and receiving
frames is &lt;a href=&quot;https://en.wikipedia.org/wiki/KISS_(amateur_radio_protocol)&quot;&gt;KISS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve not been happy with the existing KISS modems for a few reasons. The main
one is that they just convert between packets and &lt;em&gt;audio&lt;/em&gt;. I don’t want audio,
I want I/Q signals suitable for SDRs.&lt;/p&gt;

&lt;p&gt;On the transmit side it’s less of a problem for regular 1200bps AX.25, since
either the radio will turn audio into a FM-modulated signal, or if using an SDR
it’s trivial to add the audio-to-I/Q step.&lt;/p&gt;

&lt;p&gt;On transmit you do have to trigger PTT, though. You can do VOX, but it’s not
optimal.&lt;/p&gt;

&lt;p&gt;But on the receive side it’s a completely different matter. Once it’s audio,
the information about the RF signal strength is gone. It makes it impossible to
work on more advanced reception strategies such as &lt;a href=&quot;https://youtu.be/rQkBDMeODHc&quot;&gt;whole packet clock
recovery&lt;/a&gt;, or &lt;a href=&quot;https://en.wikipedia.org/wiki/Soft-decision_decoder&quot;&gt;soft decoding&lt;/a&gt;. Soft decoding would allow things
like “CRC doesn’t match, but this one bit had a very low RF signal strength, so
if flipping that bit fixes the CRC, then that’s probably correct.&lt;/p&gt;

&lt;p&gt;Once you have a pluggable KISS modem you can also innovate on making the modem
better. A simple example is to just &lt;a href=&quot;https://blog.habets.se/2023/07/Multichannel-AX.25.html&quot;&gt;run the same modem in multiple
copies&lt;/a&gt;, thereby increasing the bandwidth (both in the Hz sense
and the bps sense).&lt;/p&gt;

&lt;p&gt;Since SDRs are not bound to audio as a communication medium, they can also be
changed to use more efficient modulations. Wouldn’t it be cool to build a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Quadrature_amplitude_modulation&quot;&gt;QAM&lt;/a&gt; modulation scheme, with &lt;a href=&quot;https://en.wikipedia.org/wiki/Low-density_parity-check_code&quot;&gt;LDPC&lt;/a&gt; and “real” soft decoding?&lt;/p&gt;

&lt;p&gt;Yes, an SDR based modem does have two main challenges:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Power. SDRs don’t transmit at high power, so you need to get it through a
power amplifier.&lt;/li&gt;
  &lt;li&gt;Duplex. Most TX-capable SDRs have two antenna ports. One for TX, one for RX.
You’ll need to have two antennas, or figure out a safe way to transmit on
the same antenna without destroying the RX port.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the duplex problem, the cheap and simple solution is to use frequencies on
different bands, and put a band pass filter on the receive port, thus blocking
the transmitted power. SDR outputs are not clean, so you’ll need a filter on
the transmit path too anyway. In other words, you can just use a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Diplexer&quot;&gt;diplexer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It gets harder if RX and TX need to be on the same band, or worse, the same
exact frequency. Repeaters tend to use &lt;a href=&quot;https://en.wikipedia.org/wiki/RF_and_microwave_filter#Cavity_filters&quot;&gt;cavity filters&lt;/a&gt;. But that’s a
bit bulky for my use cases. And in any case don’t work if the frequency is
exactly the same.&lt;/p&gt;

&lt;p&gt;More likely a better use case here is to use half duplex, with a relay
switching from RX to TX and back. But you need to synchronize it so that
there’s no race condition that accidentally plows 10W into your receive port,
even for a split second.&lt;/p&gt;

&lt;p&gt;But that’s a problem for the future. For now I’m just using two antennas.&lt;/p&gt;

&lt;h2 id=&quot;how-to-run-the-modem&quot;&gt;How to run the modem&lt;/h2&gt;

&lt;p&gt;I’ve implemented it. It works. It’s &lt;a href=&quot;https://github.com/ThomasHabets/rustradio/blob/main/examples/bell202.rs&quot;&gt;less that 250 lines&lt;/a&gt; of Rust, and
the actual transmitter and receiver is really easy to follow. Well… to me at
least.&lt;/p&gt;

&lt;p&gt;In order to not introduce too many things at a time, here’s how to use the
regular Linux kernel stack with my new bell202 modem. &lt;a href=&quot;https://en.wikipedia.org/wiki/Bell_202&quot;&gt;Bell202&lt;/a&gt; is the
standard and most used amateur radio data mode. Often just referred to as
“1200bps packet”.&lt;/p&gt;

&lt;p&gt;Build and start the modem:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git clone https://github.com/ThomasHabets/rustradio
[…]
$ cd rustradio
$ cargo build --release -F soapysdr,fast-math,nix --example bell202
[…]
$ ./target/release/examples/bell202 \
    -d 'driver=uhd' \
    --tty ./mydev.link \
    --freq 144.800m \
    --ogain 0.5
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Create Linux AX.25 config:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt install libax25
$ echo 'radio1 M0XXX-1 9600 255 5 My test' | sudo tee -a /etc/ax25/axports
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Attach the kernel to the modem:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo kissattach ./mydev.link radio1
AX.25 port radio1 bound to device ax0  
$ sudo kissparms -c 1 -p radio1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now use it as normal:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ axcall M0XXX-2
[…]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</description>
        <pubDate>Sat, 21 Jun 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/06/Software-defined-KISS-modem.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/06/Software-defined-KISS-modem.html</guid>
        
        
        <category>radio</category>
        
        <category>rustradio</category>
        
      </item>
    
      <item>
        <title>QO100 early success</title>
        <description>&lt;p&gt;I have heard and been heard via QO-100! As a &lt;a href=&quot;https://en.wikipedia.org/wiki/Amateur_radio&quot;&gt;licensed radio amateur&lt;/a&gt;
I have sent signals via satellite as far away as Brazil.&lt;/p&gt;

&lt;h2 id=&quot;what-it-is&quot;&gt;What it is&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Es%27hail_2&quot;&gt;QO-100&lt;/a&gt; is the first geostationary satellite with an amateur radio
payload. A “repeater”, if you will.
Geostationary means that you just aim your antenna (dish) once, and you can use
it forever.&lt;/p&gt;

&lt;p&gt;This is amazing for tweaking and experimenting. Other amateur radio satellites
are only visible in the sky for minutes at a time, requiring you to chase
them across the sky to make a contact before it’s gone.&lt;/p&gt;

&lt;p&gt;They also fly lower, meaning they can only see &lt;a href=&quot;/static/2025-06-qo100-so50.png&quot;&gt;a small part of the world at a
time&lt;/a&gt;. QO-100 has constant line-of-sight to &lt;a href=&quot;https://amsat-uk.org/wp-content/uploads/2014/03/eshail-2-coverage-area1.png&quot;&gt;all of Africa,
Europe, India, and parts of Brazil&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;needs-a-bit-more-equipment-though&quot;&gt;Needs a bit more equipment, though&lt;/h2&gt;

&lt;p&gt;Other “birds” (satellites) can be accessed using a normal handheld FM radio and
something like an &lt;a href=&quot;https://www.arrowantennas.com/arrowii/146-437.html&quot;&gt;arrow antenna&lt;/a&gt;. Well, you should actually have two
radios, so that you can hear yourself on the downlink while transmitting.&lt;/p&gt;

&lt;p&gt;There are also linear amateur radio satellites. For them you need SSB radios,
which narrows down which radios you can use. And you still need to chase them
across the sky.&lt;/p&gt;

&lt;p&gt;For QO-100, though, you don’t just need another modulation, you need more
complicated &lt;em&gt;frequencies&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The uplink for QO-100 is 2.4GHz, and the downlink is 10.4895GHz. (I’m only
looking at the narrowband transponder, for now).&lt;/p&gt;

&lt;h2 id=&quot;my-setup&quot;&gt;My setup&lt;/h2&gt;

&lt;p&gt;There are many options. I chose mine because I want to do as much as possible
with software defined radios, and I want my components to work for other
experiments, if this one doesn’t work out.&lt;/p&gt;

&lt;h3 id=&quot;receive-side&quot;&gt;Receive side&lt;/h3&gt;

&lt;p&gt;First I needed a dish. I bought &lt;a href=&quot;https://www.passion-radio.com/satellite-qo-100/0500099-749.html&quot;&gt;a small 35cm dish from passion-radio&lt;/a&gt;,
because of its portability. Small dishes are not as good at focusing the
signal, but in return they are easier to aim, since they have a wider beam.&lt;/p&gt;

&lt;p&gt;Basically nothing reasonable can receive a 10GHz directly, so the next thing I
needed was an &lt;a href=&quot;https://en.wikipedia.org/wiki/Low-noise_block_downconverter&quot;&gt;LNB&lt;/a&gt;. It takes a block of high frequencies, and “moves it
down” to lower ones. I got the &lt;a href=&quot;https://www.passion-radio.com/lnb-pll/lnb-othernet-978.html&quot;&gt;othernet Bullseye&lt;/a&gt;, since it has good
specs and reviews. This converts the downlink signal down to a more manageable
759.5MHz. The LNB needs a bias-tee of 12V to power it. This is like a “power
over ethernet injector”, for those of you who build networks.&lt;/p&gt;

&lt;p&gt;This shifting of frequencies also has the benefit that lower frequencies have
lower losses while going through coax.&lt;/p&gt;

&lt;p&gt;I plugged this into a &lt;a href=&quot;https://www.ettus.com/all-products/ub200-kit/&quot;&gt;USRP B200&lt;/a&gt; (if buying from ettus, you’ll need &lt;a href=&quot;https://www.ettus.com/all-products/usrp-b200-enclosure/&quot;&gt;its
enclosure&lt;/a&gt; too). There are cheaper options out there, but I bought
this 10 years ago, so why not use it. I also tried &lt;a href=&quot;https://limemicro.com/boards/limesdr-mini/&quot;&gt;a LimeSDR Mini&lt;/a&gt;.
It worked very well too, but my B200 has a GPS disciplined
oscillator, which reduces any unknowns about being on frequency.&lt;/p&gt;

&lt;p&gt;After the investment of the dish and the LNB, I brought the receive setup to
the top of a tall building, to see if it works.&lt;/p&gt;

&lt;p&gt;It was harder than I expected to point the dish. It needs to be &lt;em&gt;very&lt;/em&gt;
precisely aimed. In theory you can just use &lt;a href=&quot;https://eshail.batc.org.uk/point/&quot;&gt;this website&lt;/a&gt; and a
compass, but compasses don’t like nearby metal (including the metal in the
dish), so I found that it was more effective to eyeball it from google maps,
and the alignment of nearby buildings, and hunt from there.&lt;/p&gt;

&lt;p&gt;The second problem is wind. Wind &lt;em&gt;will&lt;/em&gt; catch the dish, and tip it over. I
recommend a better plan than to simply dedicate one hand to holding on to it.&lt;/p&gt;

&lt;p&gt;Eventually I found the beacons where I expected them:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-qo100-edges.png&quot;&gt;&lt;img src=&quot;/static/2025-06-qo100-edges.png&quot; alt=&quot;Band edges&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The best way I found to fine tune the direction was to output FT8 as audio, and
maximize the apparent volume, while making very small adjustments.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-qo100-ft8.png&quot;&gt;&lt;img src=&quot;/static/2025-06-qo100-ft8.png&quot; alt=&quot;Tuned to FT8&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though the B200 was GPS locked, and presumably bang on frequency, that’s
not the case for the LNB. It’s not bad, but it looks like it’s a problem for
very frequency sensitive modes like FT8.&lt;/p&gt;

&lt;p&gt;I used pipewire virtual audio cards to get the signal from GNURadio into
WSJT-X. There we see the problem clearly. Those yellow blocks at the bottom
should be perfect rectangles, and not lean to the side. The fact that they all
lean the same way tells me it’s a problem on my end, not with the person
transmitting.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-qo100-ft8-skew.png&quot;&gt;&lt;img src=&quot;/static/2025-06-qo100-ft8-skew.png&quot; alt=&quot;FT8 skew in wsjtx&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a plan for fixing this, but not today.&lt;/p&gt;

&lt;p&gt;So in short: LNB in dish - bias-tee - USRP B200.&lt;/p&gt;

&lt;h3 id=&quot;transmit-side&quot;&gt;Transmit side&lt;/h3&gt;

&lt;p&gt;The transmit side has a different problem than the receive side. 2.4GHz is easy
enough to generate using an SDR like the B200 (or LimeSDR). But the output of
SDRs doesn’t have any power.&lt;/p&gt;

&lt;p&gt;Before amplifying, though, it needs to be filtered. SDRs can generate any
signal in a huge frequency range, but in the end any transmitter needs an
analog filter. And analog filters are physical. They are band specific. I found
some nice &lt;a href=&quot;https://www.aliexpress.com/item/1005007509062592.html&quot;&gt;band pass filters&lt;/a&gt; on aliexpress, and bought a bunch for
different bands. For this project we need a 2.4GHz band pass filter.&lt;/p&gt;

&lt;p&gt;For power amplification I bought a &lt;a href=&quot;https://dxpatrol.pt/produto/power-amplifier/&quot;&gt;10W amplifier&lt;/a&gt;, but I’m still
trying to make it work. In the mean time, I decided to use an &lt;a href=&quot;https://www.mouser.co.uk/new/analog-devices/adi-eval-cn0417-ebz-rf-power-amplifier/&quot;&gt;analog devices
CN0417&lt;/a&gt;. It’s only a 1W amplifier, but I was hoping that it could work
for morse code or FT8.&lt;/p&gt;

&lt;p&gt;In the past I’ve noticed that the USRP B200 has produced a pretty garbage
signal (“nonlinearities”, the word may be) when it has its output gain set
too high. I can set the gain lower, but the 1W power amplifier only amplifies
by 20dB. So I added a 30dB &lt;a href=&quot;https://www.aliexpress.com/item/1005007540480401.html&quot;&gt;LNA&lt;/a&gt; before the power amplifier.&lt;/p&gt;

&lt;p&gt;I have only used LNAs on the receive side before, but turns out they work very
well to amplify the low power of an SDR for transmit, too.&lt;/p&gt;

&lt;p&gt;For the antenna, the best option seems to be to get a &lt;a href=&quot;https://dxpatrol.pt/produto/helix-high-performance/&quot;&gt;helix antenna&lt;/a&gt;,
since it sits “around” the LNB, and bounces on the same dish.&lt;/p&gt;

&lt;p&gt;In short: B200 - 30dB LNA - CN0417 - Helix antenna.&lt;/p&gt;

&lt;h2 id=&quot;converting-to-and-from-audio&quot;&gt;Converting to and from audio&lt;/h2&gt;

&lt;p&gt;At this point I have the uplink and downlink signal in the B200. As one does, I
created &lt;a href=&quot;/static/2025-06-qo100-live.grc&quot;&gt;a mess of a GNU Radio graph&lt;/a&gt;. It only uses standard blocks.&lt;/p&gt;

&lt;p&gt;The transmit side takes audio and just sends it to the SDR. For transmit the
SDR is mostly easily tuned directly. It just needs analog filtering and power
amplification.&lt;/p&gt;

&lt;p&gt;The receive side takes the whole narrowband downlink, and lets me tune around
for what I want to decode. Or save the whole spectrum to disk.&lt;/p&gt;

&lt;p&gt;To connect to WSJT-X I used the same &lt;a href=&quot;https://blog.habets.se/2021/12/Virtual-audio-cables.html&quot;&gt;virtual audio cables&lt;/a&gt; I’ve blogged
about in the past. Some fighting in &lt;code class=&quot;highlighter-rouge&quot;&gt;pavucontrol&lt;/code&gt; is needed to route the audio
to the right loopback.&lt;/p&gt;

&lt;p&gt;WSJT-X doesn’t need to trigger PTT, since this setup is full duplex.&lt;/p&gt;

&lt;h2 id=&quot;the-location&quot;&gt;The location&lt;/h2&gt;

&lt;p&gt;Sure, a bigger dish would help, as would putting the dish on the roof. But
since I need to tweak it and have short cables for efficiency, putting
it on the roof means a risk I’m not willing to take. So at least for now the
setup is near ground level.&lt;/p&gt;

&lt;h2 id=&quot;how-did-it-go&quot;&gt;How did it go?&lt;/h2&gt;

&lt;p&gt;Reception works better than expected. Sure, not as clean as &lt;a href=&quot;https://eshail.batc.org.uk/nb/&quot;&gt;some other ground
stations&lt;/a&gt;, but that’s to be expected.&lt;/p&gt;

&lt;p&gt;For transmit, I first thought the small dish and weak amplifier was just not
enough. I could not see the &lt;a href=&quot;https://github.com/ThomasHabets/rustradio/blob/main/examples/morse_beacon.rs&quot;&gt;morse code beacons I send using
RustRadio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But then I saw and heard the beacons on websdr! So they do get there.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-qo100-heard-cw.png&quot;&gt;&lt;img src=&quot;/static/2025-06-qo100-heard-cw.png&quot; alt=&quot;Heard morse code beacons&quot; /&gt;&lt;/a&gt;
(&lt;a href=&quot;/static/2025-06-qo100-morse.wav&quot;&gt;sound&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;What about FT8?&lt;/p&gt;

&lt;p&gt;As mentioned above, FT8 decoding isn’t great. Some decode, but most (by far)
don’t. They sound OK, but don’t decode. I tried switching to lower sideband, in
case that’s how people transmitted, but that didn’t seem to help. I think it’s
all due to needing to compensate for LNB frequency instability.&lt;/p&gt;

&lt;p&gt;I was heard, though:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-06-qo100-ft8-reports.png&quot;&gt;&lt;img src=&quot;/static/2025-06-qo100-ft8-reports.png&quot; alt=&quot;FT8 heard all over&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So yes, I can receive stronger signals, and I can be heard with my weaker
signals. I just can’t hear my own weak signals.&lt;/p&gt;

&lt;h2 id=&quot;rooms-for-improvement&quot;&gt;Rooms for improvement&lt;/h2&gt;

&lt;p&gt;A bigger dish would help. I just need to figure out the best way to install it.
Maybe this can wait until it’s all “perfect” and I can hire someone to install
it on the roof.&lt;/p&gt;

&lt;p&gt;More power. I need to get this 10W amplifier working. That should make me able
to hear myself.&lt;/p&gt;

&lt;p&gt;I need to figure out the downlink frequency stability. Either I can try to get
an LNB with an external GPS discliplined clock, or I could use the satellite’s
beacons — presumably stable — to lock on and fix the LNB instability in
software. That’d be ideal. What can be done in software should be done in
software.&lt;/p&gt;

&lt;h2 id=&quot;the-station&quot;&gt;The station&lt;/h2&gt;

&lt;p&gt;In use:
&lt;a href=&quot;/static/2025-06-qo100-setup.png&quot;&gt;&lt;img src=&quot;/static/2025-06-qo100-setup.png&quot; alt=&quot;Photo of the station in use&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mocked up:
&lt;a href=&quot;/static/2025-06-qo100-setup2.png&quot;&gt;&lt;img src=&quot;/static/2025-06-qo100-setup2.png&quot; alt=&quot;Photo of the station mocked up&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Sun, 15 Jun 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/06/QO100-early-success.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/06/QO100-early-success.html</guid>
        
        
        <category>radio</category>
        
      </item>
    
      <item>
        <title>io_uring, kTLS and Rust for zero syscall HTTPS server</title>
        <description>&lt;p&gt;Around the turn of the century we started to get a bigger need for high
capacity web servers. For example there was &lt;a href=&quot;https://en.wikipedia.org/wiki/C10k_problem&quot;&gt;the C10k problem&lt;/a&gt; paper.&lt;/p&gt;

&lt;p&gt;At the time, the kinds of things done to reduce work done per request was
pre-forking the web server. This means a request could be handled without an
expensive process creation.&lt;/p&gt;

&lt;p&gt;Because yes, creating a new process for every request used to be something
perfectly normal.&lt;/p&gt;

&lt;p&gt;Things did get better. People learned how to create threads, making things more
light weight. Then they switched to using &lt;code class=&quot;highlighter-rouge&quot;&gt;poll()&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;select()&lt;/code&gt;, in order to not
just spare the process/thread creation, but the whole context switch.&lt;/p&gt;

&lt;p&gt;I remember a comment on &lt;a href=&quot;https://en.wikipedia.org/wiki/Kuro5hin&quot;&gt;Kuro5hin&lt;/a&gt; from anakata, the creator of both The
Pirate Bay and the web server that powered it, along the lines of “I am select()
of borg, resistance is futile”, mocking someone for not understanding how to
write a scalable web server.&lt;/p&gt;

&lt;p&gt;But &lt;code class=&quot;highlighter-rouge&quot;&gt;select()&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;poll()&lt;/code&gt; also doesn’t scale. If you have ten thousand
connections, that’s an array of ten thousand integers that need to be sent to
the kernel for every single iteration of your request handling loop.&lt;/p&gt;

&lt;p&gt;Enter &lt;code class=&quot;highlighter-rouge&quot;&gt;epoll&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;kqueue&lt;/code&gt; on other operating systems, but I’m focusing on Linux
here). Now that’s better. The main loop is now:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  set_up_epoll()
  while True:
    new, read, write = epoll()
    epoll_add_connections(new)
    for con in read:
      process(con.read())
      if con.read_all_we_need:
        epoll_remove_read_op(con)
    for con in write:
      con.write_buffer()
      if con.buffer_empty:
        epoll_remove_write_op(con)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;All the syscalls are pretty cheap. &lt;code class=&quot;highlighter-rouge&quot;&gt;epoll()&lt;/code&gt; only deals in deltas, and it
doesn’t have to be re-told the thousands of active connections.&lt;/p&gt;

&lt;p&gt;But they’re not without cost. Once we’ve gotten this far, the cost of a syscall
is actually a significant part of the total remaining cost.&lt;/p&gt;

&lt;p&gt;We’re here going to ignore improvements like &lt;code class=&quot;highlighter-rouge&quot;&gt;sendfile()&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;splice()&lt;/code&gt;, and
instead jump to…&lt;/p&gt;

&lt;h2 id=&quot;iouring&quot;&gt;io_uring&lt;/h2&gt;

&lt;p&gt;Instead of performing a syscall for everything we want to do, commanding the
kernel to do this or that, io_uring lets us just keep writing orders to a
queue, and letting the kernel consume that queue asynchronously.&lt;/p&gt;

&lt;p&gt;For example, we can put &lt;code class=&quot;highlighter-rouge&quot;&gt;accept()&lt;/code&gt; into the queue. The kernel will pick that
up, wait for an incoming connection, and when it arrives it’ll put a
“completion” into the completion queue.&lt;/p&gt;

&lt;p&gt;The web server can then check the completion queue. If there’s a completion
there, it can act on it.&lt;/p&gt;

&lt;p&gt;This way the web server can queue up all kinds of operations that were
previously “expensive” syscalls by simply writing them to memory. That’s it.
And then it’ll read the results from another part of memory. That’s it.&lt;/p&gt;

&lt;p&gt;In order to avoid busy looping, both the kernel and the web server will only
busy-loop checking the queue for a little bit (configurable, but think
milliseconds), and if there’s nothing new, the web server will do a syscall to
“go to sleep” until something gets added to the queue.&lt;/p&gt;

&lt;p&gt;Similarly on the kernel side, the kernel will stop busy-looping if there’s
nothing new, and needs a syscall to start busylooping again.&lt;/p&gt;

&lt;p&gt;This sounds like it would be tricky to optimize, but it’s not. In the end the
web server just puts stuff on the queue, and calls a library function that only
does that syscall if the kernel actually has stopped busylooping.&lt;/p&gt;

&lt;p&gt;This means that a busy web server can serve all of its queries without even once
(after setup is done) needing to do a syscall. As long as queues keep getting
added to, &lt;code class=&quot;highlighter-rouge&quot;&gt;strace&lt;/code&gt; will show &lt;em&gt;nothing&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;one-thread-per-core&quot;&gt;One thread per core&lt;/h2&gt;

&lt;p&gt;Since CPUs today have many cores, ideally you want to run exactly one thread
per core, bind it to that core, and not share any read-write data structure.&lt;/p&gt;

&lt;p&gt;For &lt;a href=&quot;https://en.wikipedia.org/wiki/Non-uniform_memory_access&quot;&gt;NUMA&lt;/a&gt; hardware, you also want to make sure that a thread only
accesses memory on the local NUMA node. &lt;a href=&quot;https://youtu.be/36qZYL5RlgY&quot;&gt;This netflix talk&lt;/a&gt; has some
interesting stuff on NUMA and high volume HTTP delivery.&lt;/p&gt;

&lt;p&gt;The request load will still not be perfectly balanced between the threads (and
therefore cores), but I guess fixing that would have to be the topic of a
future post.&lt;/p&gt;

&lt;h2 id=&quot;memory-allocations&quot;&gt;Memory allocations&lt;/h2&gt;

&lt;p&gt;We will still have memory allocations though, both on the kernel and web server
side. Memory allocations in user space will eventually need syscalls.&lt;/p&gt;

&lt;p&gt;For the web server side, you can pre-allocate a fixed chunk for every
connection, and then have everything about that connection live there. That way
new connections don’t need syscalls, memory doesn’t get fragmented, and you
don’t run the risk of running out of memory.&lt;/p&gt;

&lt;p&gt;On the kernel side each connection will still need buffers for incoming and
outgoing bytes. This may be somewhat controllable via socket options, but again
it’ll have to be the subject of a future post.&lt;/p&gt;

&lt;p&gt;Try to not run out of RAM. Bad things tend to happen.&lt;/p&gt;

&lt;h2 id=&quot;ktls&quot;&gt;kTLS&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.kernel.org/networking/tls-offload.html&quot;&gt;kTLS&lt;/a&gt; is a feature of the Linux kernel where an application can hand off
the job of encryption/decryption to the kernel. The application still has to
perform the TLS handshake, but after that it can enable kTLS and pretend that
it’s all sent in plaintext.&lt;/p&gt;

&lt;p&gt;You may say that this doesn’t actually speed anything up, it just moves &lt;em&gt;where&lt;/em&gt;
encryption was done. But there are gains:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;This means that &lt;code class=&quot;highlighter-rouge&quot;&gt;sendfile()&lt;/code&gt; can be used, removing the need to copy a bunch
of data between user space and kernel space.&lt;/li&gt;
  &lt;li&gt;If the network card has hardware support for it, the crypto operation may
actually be offloaded from the CPU onto the network card, leaving the CPU to
do better things.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;descriptorless-files&quot;&gt;Descriptorless files&lt;/h2&gt;

&lt;p&gt;Another optimization is to avoid passing file descriptors back and forth
between user space and kernel space. The mapping between file descriptors and
io_uring apparently has overhead.&lt;/p&gt;

&lt;p&gt;So in comes &lt;a href=&quot;https://lwn.net/Articles/863071/&quot;&gt;descriptorless files&lt;/a&gt; via
&lt;a href=&quot;https://docs.rs/io-uring/latest/io_uring/struct.Submitter.html#method.register_files&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;register_files&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now the supposed file descriptor numbers that user space sees are just
integers. They don’t show up in &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/pid/fd&lt;/code&gt;, and can only be used with
io_uring. They’re still capped by the &lt;code class=&quot;highlighter-rouge&quot;&gt;ulimit&lt;/code&gt; file descriptor limit, though.&lt;/p&gt;

&lt;h2 id=&quot;tarweb&quot;&gt;tarweb&lt;/h2&gt;

&lt;p&gt;In order to learn these technologies better, I built &lt;a href=&quot;https://github.com/ThomasHabets/tarweb&quot;&gt;a web server
incorporating all these things&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s named &lt;code class=&quot;highlighter-rouge&quot;&gt;tarweb&lt;/code&gt; because it’s a web server that serves the content of a
single tar file.&lt;/p&gt;

&lt;p&gt;Rust, io_uring, and kTLS. Not exactly the most common combination. I found that
io_uring and kTLS didn’t play super well together. Enabling kTLS requires three
&lt;code class=&quot;highlighter-rouge&quot;&gt;setsockopt()&lt;/code&gt; calls, and io_uring doesn’t support &lt;code class=&quot;highlighter-rouge&quot;&gt;setsockopt&lt;/code&gt; (until they
merge &lt;a href=&quot;https://github.com/tokio-rs/io-uring/pull/320&quot;&gt;my PR&lt;/a&gt;, that is).&lt;/p&gt;

&lt;p&gt;And the &lt;code class=&quot;highlighter-rouge&quot;&gt;ktls&lt;/code&gt; crate, part of &lt;code class=&quot;highlighter-rouge&quot;&gt;rustls&lt;/code&gt;, only allows you to call the synchronous
&lt;code class=&quot;highlighter-rouge&quot;&gt;setsockopt()&lt;/code&gt;, not export the needed struct for me to pass to my new
io_uring &lt;code class=&quot;highlighter-rouge&quot;&gt;setsockopt&lt;/code&gt;. &lt;a href=&quot;https://github.com/rustls/ktls/pull/54&quot;&gt;Another pr sent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So with those two PRs merged, it’s working great.&lt;/p&gt;

&lt;p&gt;tarweb is far from perfect. The code needs a lot of work, and there’s no
guarantee that the TLS library (rustls) doesn’t do memory allocations during
handshakes. But it does serve https without even one syscall on a per request
basis. And that’s pretty cool.&lt;/p&gt;

&lt;h2 id=&quot;benchmarks&quot;&gt;Benchmarks&lt;/h2&gt;

&lt;p&gt;I have not done any benchmarks yet. I want to clean the code up first.&lt;/p&gt;

&lt;h2 id=&quot;io-uring-and-safety&quot;&gt;io-uring and safety&lt;/h2&gt;

&lt;p&gt;One thing making io_uring more complex than synchronous syscalls is that any
buffer needs to stay in memory until the operation is marked completed by
showing up in the completion queue.&lt;/p&gt;

&lt;p&gt;For example when submitting a &lt;code class=&quot;highlighter-rouge&quot;&gt;write&lt;/code&gt; operation, the memory location of those
bytes must not be deallocated or overwritten.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;io-uring&lt;/code&gt; crate doesn’t help much with this. The API doesn’t allow the
borrow checker to protect you at compile time, and I don’t see it doing any
runtime checks either.&lt;/p&gt;

&lt;p&gt;I feel like I’m back in C++, where any mistake can blow your whole leg off.
It’s a miracle that I’ve not seen a segfault.&lt;/p&gt;

&lt;p&gt;Someone should make a &lt;code class=&quot;highlighter-rouge&quot;&gt;safer-ring&lt;/code&gt; crate or similar, using the powers of
&lt;a href=&quot;https://blog.cloudflare.com/pin-and-unpin-in-rust/&quot;&gt;pinning&lt;/a&gt; and/or borrows or something, to achieve Rust’s normal “if it
compiles, then it’s correct”.&lt;/p&gt;

&lt;p&gt;Edit: I’m &lt;a href=&quot;https://boats.gitlab.io/blog/post/io-uring/&quot;&gt;not the first to point it
out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post was &lt;a href=&quot;https://news.ycombinator.com/item?id=44980865&quot;&gt;discussed on HackerNews on
2025-08-22&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Sun, 06 Apr 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html</guid>
        
        
        <category>network</category>
        
        <category>rust</category>
        
        <category>tls</category>
        
      </item>
    
      <item>
        <title>Exploring RISC-V vector instructions</title>
        <description>&lt;p&gt;It finally happened! A raspberry pi like device, with a RISC-V CPU supporting
the &lt;code class=&quot;highlighter-rouge&quot;&gt;v&lt;/code&gt; extension. Aka &lt;code class=&quot;highlighter-rouge&quot;&gt;RVV&lt;/code&gt;. Aka vector instructions.&lt;/p&gt;

&lt;p&gt;I bought one, and explored it a bit.&lt;/p&gt;

&lt;h2 id=&quot;simd-background&quot;&gt;SIMD background&lt;/h2&gt;

&lt;p&gt;First some background on SIMD.&lt;/p&gt;

&lt;p&gt;SIMD is a set of instructions allowing you to do the same operation to multiple
independent pieces of data. As an example, say you had four 8 bit integers, and
you wanted to multiply them all by 2, then add 1. You could do this with a
single operation without any special instructions.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    # x86 example assembly.

    mov eax, [myvalues]  # load our four bytes.
    mov ebx, 2           # we want to multiply by two
    imul eax, ebx        # single operation, multiple data!
                         # After this, eax contains 0x02040608
    add eax, 0x01010101  # single operation, multiple data!
                         # After this, eax contains 0x03050709
    mov [myvalues], eax  # store back the new value.

section .data
  myvalues db 1,2,3,4
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Success, right? No, of course not. This naive code doesn’t handle
over/underflow, and doesn’t even remotely work for floating point data. For
that, we need special SIMD instructions.&lt;/p&gt;

&lt;p&gt;x86 and ARM have gone the way of fixed sized registers. In 1997 Intel introduced
&lt;a href=&quot;https://en.wikipedia.org/wiki/MMX_(instruction_set)&quot;&gt;MMX&lt;/a&gt;, to great fanfare. The PR went all “it’s multimedia!”.
“Multimedia” was a buzzword at the time. This first generation gave you a
whopping 64 bit register size, that you could use for one 64-bit value, two
32-bit values, four 16-bit values, or 8 8-bit values.&lt;/p&gt;

&lt;p&gt;A “batch size” of 64 bit, if you prefer.&lt;/p&gt;

&lt;p&gt;These new registers got a new set of instructions, to do these SIMD operations.
I’m not going to learn the original MMX instructions, but it should look
something like this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  movq mm0, [myvalues]  # load values
  movq mm1, [addconst]  # load our const addition values.
  paddb mm0, mm0        # add to itself means multiply by 2
  paddb mm0, mm1        # Add vector of ones.
  movq [myvalues], mm0  # store the updated value.
  emms                  # state reset.

section .data
  myvalues db 1,2,3,4
  addconst db 1,1,1,1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;So far so good.&lt;/p&gt;

&lt;h2 id=&quot;the-problem-with-simd&quot;&gt;The problem with SIMD&lt;/h2&gt;

&lt;p&gt;The problem with SIMD is that it’s so rigid. With MMX, the registers are 64
bits. No more, no less. Intel followed up with SSE, adding floating point
support and doubling the register size to 128 bits. That’s four 32-bit floats
in one &lt;code class=&quot;highlighter-rouge&quot;&gt;xmm&lt;/code&gt; register.&lt;/p&gt;

&lt;p&gt;So now we have 64-bit &lt;code class=&quot;highlighter-rouge&quot;&gt;mm&lt;/code&gt; registers, 128-bit &lt;code class=&quot;highlighter-rouge&quot;&gt;xmm&lt;/code&gt; registers, and uncountably
many instructions to work with these two sets of new registers.&lt;/p&gt;

&lt;p&gt;Then we got &lt;code class=&quot;highlighter-rouge&quot;&gt;SSE2&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;SSE3&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;SSE4&lt;/code&gt;. Then &lt;code class=&quot;highlighter-rouge&quot;&gt;AVX&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;AVX2&lt;/code&gt; (256 bit registers), and
even &lt;code class=&quot;highlighter-rouge&quot;&gt;AVX-512&lt;/code&gt; (512 bit registers).&lt;/p&gt;

&lt;p&gt;512 bit registers. Not bad. You can do 16 32-bit floating point operations per
instruction with that.&lt;/p&gt;

&lt;p&gt;But here’s the problem: Only if your code was written to be aware of these new
registers and instructions! If your production environment uses the best of the
best, with AVX-512, you still can’t use that, if your development/QA
environment only has AVX2. Not without having separate binaries.&lt;/p&gt;

&lt;p&gt;Or you could maintain 20 different compiled versions, and dynamically choose
between them. That’s what &lt;a href=&quot;https://wiki.gnuradio.org/index.php/Volk&quot;&gt;volk&lt;/a&gt; does. Or you could compile with
&lt;code class=&quot;highlighter-rouge&quot;&gt;-march=native&lt;/code&gt; (gcc) or &lt;code class=&quot;highlighter-rouge&quot;&gt;-Ctarget-cpu=native&lt;/code&gt; (Rust), and create binaries that
only work on machines at least as new as the one you built on.&lt;/p&gt;

&lt;p&gt;But none of these options allow you to build a binary that will automatically
take advantage of &lt;strong&gt;future&lt;/strong&gt; processor improvements.&lt;/p&gt;

&lt;p&gt;Vector instructions do.&lt;/p&gt;

&lt;h2 id=&quot;vector-instructions&quot;&gt;Vector instructions&lt;/h2&gt;

&lt;p&gt;Instead of working with fixed sized batches, vector instructions let you specify
the size of the &lt;em&gt;data&lt;/em&gt;, and the CPU will do as many at once as it can.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  # Before we enter the loop, input registers are set thusly:
  # a0: number of elements to process.
  # a1: pointer to input elements.
  # a2: pointer to output elements.

  # Load 2.0 once. We'll need it later.
  flw ft0, two
loop:
  # prepare the CPU to process a batch:
  # * a0: of *up to* a0 elements,
  # * e32: each element is 32 bits. The spec calls this SEW.
  # * m1: group registers in size of 1 (I'll get to this). LMUL in the spec.
  # * ta &amp;amp; ma: ignore these flags, they're deep details.
  #
  # t0 will be set to the number of elements in a &quot;batch&quot;
  vsetvli t0, a0, e32, m1, ta, ma

  # Set t1 to be the number of bytes per &quot;batch&quot;.
  # t1 = t0 &amp;lt;&amp;lt; 2
  slli t1, t0, 2

  # Load a batch.
  vle32.v v0, (a0)

  # Multiply them all by 2.0.
  vfmul.vf v0, v0, ft0

  # Store them in the output buffer.
  vse32.v v0, (a2)

  # Update pointers and element counters.
  add a1, a1, t1
  add a2, a2, t1
  sub a0, a0, t0

  # Loop until a0 is zero.
  bnez a0, loop

two:    .float 2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Write once, and when vector registers get bigger, your code will automatically
perform more multiplies per batch. That’s great! You can use an old and slow
RISC-V CPU for development, but then when you get your big beefy machine the
code is ready to go full speed.&lt;/p&gt;

&lt;p&gt;The RISC-V vector spec allows for vector registers up to 64Kib=8KiB, or 2048
32-bit floats. And with &lt;code class=&quot;highlighter-rouge&quot;&gt;m8&lt;/code&gt; (see below), that allows e.g. 16384 32-bit floats
being multiplied by 16384 other floats, and then added added to yet more 16384
floats, in a single &lt;a href=&quot;https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation&quot;&gt;fused multiply-add&lt;/a&gt; instruction.&lt;/p&gt;

&lt;h2 id=&quot;even-more-batching&quot;&gt;Even more batching&lt;/h2&gt;

&lt;p&gt;RISC-V has 32 vector registers. On a particular CPU, each register will be the
same fixed size, called &lt;code class=&quot;highlighter-rouge&quot;&gt;VLEN&lt;/code&gt;. But the instruction set allows us to group
the registers, creating mega-registers. That’s what the &lt;code class=&quot;highlighter-rouge&quot;&gt;m1&lt;/code&gt; in &lt;code class=&quot;highlighter-rouge&quot;&gt;setvli&lt;/code&gt; is
about.&lt;/p&gt;

&lt;p&gt;If we use &lt;code class=&quot;highlighter-rouge&quot;&gt;m8&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;m1&lt;/code&gt;, that gives you just four vector registers: v0,
v8, v16, and v24. But in return they are 8 times as wide.&lt;/p&gt;

&lt;p&gt;The spec calls this batching number &lt;code class=&quot;highlighter-rouge&quot;&gt;LMUL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Basically a pairwise floating point vector multiplication &lt;code class=&quot;highlighter-rouge&quot;&gt;vfmul.vf v0, v0, v8&lt;/code&gt;
in &lt;code class=&quot;highlighter-rouge&quot;&gt;m8&lt;/code&gt; mode effectively represents:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   vfmul.vf v0, v0, v8
   vfmul.vf v1, v1, v9
   vfmul.vf v2, v2, v10
   vfmul.vf v3, v3, v11
   vfmul.vf v4, v4, v12
   vfmul.vf v5, v5, v13
   vfmul.vf v6, v6, v14
   vfmul.vf v7, v7, v15
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Bigger batching, at the cost of fewer registers. I couldn’t come up with a nice
way to multiply two complex numbers with only four registers. Maybe you can? If
so, please send a PR adding a &lt;code class=&quot;highlighter-rouge&quot;&gt;mul_cvec_asm_m8_segment&lt;/code&gt; function to &lt;a href=&quot;https://github.com/ThomasHabets/rvv-vroom&quot;&gt;my
repo&lt;/a&gt;. Until then, the &lt;code class=&quot;highlighter-rouge&quot;&gt;m4&lt;/code&gt; version is the biggest batch. &lt;code class=&quot;highlighter-rouge&quot;&gt;m8&lt;/code&gt; may
still not be faster, since the &lt;code class=&quot;highlighter-rouge&quot;&gt;m2&lt;/code&gt; version of &lt;code class=&quot;highlighter-rouge&quot;&gt;mul_cvec_asm_mX_segment&lt;/code&gt; is
a little bit faster than the &lt;code class=&quot;highlighter-rouge&quot;&gt;m4&lt;/code&gt; version in my test.&lt;/p&gt;

&lt;p&gt;Like with SIMD, there are convenient vector instructions for loading and
handling data. For example, if you have a vector of complex floats, then you
probably have real and imaginary values alternating. &lt;code class=&quot;highlighter-rouge&quot;&gt;vlseg2e32.v v0, a0&lt;/code&gt; will
then load the real values in v0, and the imaginary values in v1.&lt;/p&gt;

&lt;p&gt;Or, if &lt;code class=&quot;highlighter-rouge&quot;&gt;vsetvli&lt;/code&gt; was called with &lt;code class=&quot;highlighter-rouge&quot;&gt;m4&lt;/code&gt;, the real values will be in v0 through v3,
and imaginary values in v4 through v7.&lt;/p&gt;

&lt;p&gt;Curiously a “stride” load (&lt;code class=&quot;highlighter-rouge&quot;&gt;vlse32.v v0, (a0), t1&lt;/code&gt;), where you can do things
like “load every second float”, seems to not have very good performance.
Maybe this is specific to the CPU I’m using. I would have expected the L1 cache
to make them fairly equal, but apparently not.&lt;/p&gt;

&lt;p&gt;So yes, it’s not perfect. On a future CPU it may be cheap to load from L1 cache,
so your code should be more wasteful about vector registers, to be optimal.
Maybe on a future CPU the stride load is faster than the segmented load. There’s
no way to know.&lt;/p&gt;

&lt;h2 id=&quot;the-orange-pi-rv2&quot;&gt;The Orange Pi RV2&lt;/h2&gt;

&lt;p&gt;It seems that the CPU, a Ky X1, isn’t known to llvm yet. So you have to manually
enable the &lt;code class=&quot;highlighter-rouge&quot;&gt;v&lt;/code&gt; extension when compiling. But that’s fine.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat ~/.cargo/config.toml
[target.riscv64gc-unknown-linux-gnu]
rustflags = [&quot;-Ctarget-cpu=native&quot;, &quot;-Ctarget-feature=+v&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;I filed &lt;a href=&quot;https://github.com/rust-lang/rust/issues/138789&quot;&gt;a bug&lt;/a&gt; with Rust
about it, but it seems it may be a missing LLVM feature. It’s apparently not
merely checking &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/cpuinfo&lt;/code&gt; for the features in question, but needs the
name of the CPU in code or something.&lt;/p&gt;

&lt;p&gt;It seems that the vector registers (VLEN) on this hardware are 256 bit wide.
This means that with &lt;code class=&quot;highlighter-rouge&quot;&gt;m8&lt;/code&gt; a single multiplication instruction can do
&lt;code class=&quot;highlighter-rouge&quot;&gt;8*256/32=64&lt;/code&gt; 32-bit floating point operations. Multiplying two vector registers
in one instructions multiplies half a kibibyte (256 bytes per aggregate
register).&lt;/p&gt;

&lt;p&gt;64 32bit operations is a lot. We started off in SIMD with just 2. And as I say
above, when future hardware gets bigger vector registers, you won’t even have
to recompile.&lt;/p&gt;

&lt;p&gt;That’s not to say that the Orange Pi RV2 is some sort of supercomputer. It’s
much faster than the &lt;a href=&quot;https://blog.habets.se/2023/01/VisionFive-2-quickstart.html&quot;&gt;VisionFive 2&lt;/a&gt;, but your laptop is much faster
still.&lt;/p&gt;

&lt;h2 id=&quot;so-how-much-faster-is-it&quot;&gt;So how much faster is it?&lt;/h2&gt;

&lt;p&gt;I started &lt;a href=&quot;https://github.com/ThomasHabets/rvv-vroom&quot;&gt;a Rust crate&lt;/a&gt; to test this out.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo +nightly bench --target  target-riscv64-no-vector.json -Zbuild-std
[…]
running 10 tests
test bench_mul_cvec_asm_m2_segment ... bench:       2,930.20 ns/iter (+/- 29.96)
test bench_mul_cvec_asm_m4_segment ... bench:       3,036.18 ns/iter (+/- 100.35)
test bench_mul_cvec_asm_m4_stride  ... bench:       4,713.20 ns/iter (+/- 55.09)
test bench_mul_cvec_asm_m8_stride  ... bench:       5,368.08 ns/iter (+/- 15.18)
test bench_mul_cvec_rust           ... bench:       9,957.66 ns/iter (+/- 76.39)
test bench_mul_cvec_rust_v         ... bench:       3,020.23 ns/iter (+/- 21.72)
test bench_mul_fvec_asm_m4         ... bench:         843.94 ns/iter (+/- 22.86)
test bench_mul_fvec_asm_m8         ... bench:         801.36 ns/iter (+/- 27.71)
test bench_mul_fvec_rust           ... bench:       4,097.09 ns/iter (+/- 29.77)
test bench_mul_fvec_rust_v         ... bench:       1,084.47 ns/iter (+/- 13.46)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The “rust” version is normal Rust code. The &lt;code class=&quot;highlighter-rouge&quot;&gt;_v&lt;/code&gt; code is Rust code where the
compiler is allowed to use vector instructions. As you can see, Rust (well,
LLVM) is already pretty good. But my hand coded vector assembly is faster
still.&lt;/p&gt;

&lt;p&gt;As you can see, the answer for this small benchmark is “about 3-5 times faster”.
That multiplier probably goes up the more operations that you do. These
benchmarks just do a couple of multiplies.&lt;/p&gt;

&lt;p&gt;Note that I’m cheating a bit with the manual assembly. It assumes that the
input is an even multiple of the batch size.&lt;/p&gt;

&lt;h2 id=&quot;custom-target-with-rust&quot;&gt;Custom target with Rust&lt;/h2&gt;

&lt;p&gt;To experiment with target settings, I modified the target spec. This is not
necessary for normal code (you can just force-enable the &lt;code class=&quot;highlighter-rouge&quot;&gt;v&lt;/code&gt; extension, per
above), but could be interesting to know. In my case I’m actually using it to
turn vectorization &lt;em&gt;off&lt;/em&gt; by default, since while Rust lets you enable a target
feature per function, it doesn’t let you &lt;em&gt;disable&lt;/em&gt; it per function.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rustup install nightly
rustup default nightly
rustc  -Zunstable-options \
  --print target-spec-json \
  --target=riscv64gc-unknown-linux-gnu \
  &amp;gt; mytarget.json
rustup default stable
# edit mytarget.json
rustup component add --toolchain nightly rust-src
cargo +nightly bench --target mytarget.json -Zbuild-std
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;did-anything-not-just-work&quot;&gt;Did anything &lt;em&gt;not&lt;/em&gt; “just work”?&lt;/h2&gt;

&lt;p&gt;I have found that the documentation for the RISC-V vector instructions are a bit
lacking, to say the least. I’m used to reading specs, but &lt;a href=&quot;https://github.com/riscvarchive/riscv-v-spec/blob/master/v-spec.adoc&quot;&gt;this one&lt;/a&gt; is a
bit extreme.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;vlseg2e32.v v0, (a0)&lt;/code&gt; fails with illegal instructions when in &lt;code class=&quot;highlighter-rouge&quot;&gt;m8&lt;/code&gt; mode. 
That’s strange. Works fine in &lt;code class=&quot;highlighter-rouge&quot;&gt;m1&lt;/code&gt; through &lt;code class=&quot;highlighter-rouge&quot;&gt;m4&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Can we check the specs on the CPU? Doesn’t look like it. It’s a “Ky X1”. That’s
all we’re told. Is it truly RVV 1.0? Don’t know. Who even makes it? Don’t know.
I see guesses and &lt;a href=&quot;https://github.com/rust-lang/rust/issues/138789#issuecomment-2745454455&quot;&gt;assertions&lt;/a&gt; that it may be by SpacemiT, but they
don’t list it on their website. Maybe it’s a variant of the K1? Could be. The
English page doesn’t load, but the Chinese page seems to have similar marketing
phrases as Orange Pi RV2 uses for its CPU.&lt;/p&gt;

&lt;p&gt;Ah, maybe this is blocked by the spec:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The EMUL setting must be such that EMUL * NFIELDS ≤ 8, otherwise the
instruction encoding is reserved.
[…]
This constraint makes this total no larger than 1/4 of the architectural
register file, and the same as for regular operations with EMUL=8.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So for some reason you can’t load half the register space in a single
instruction. Oh well, I guess I have to settle for loading 256 bytes at a time.&lt;/p&gt;

&lt;p&gt;It’s a strange requirement, though. The instruction encoding allows it, but it
just doesn’t do the obvious thing.&lt;/p&gt;

&lt;h2 id=&quot;arm&quot;&gt;ARM&lt;/h2&gt;

&lt;p&gt;ARM uses SIMD like Intel. I vaguely remember that it also has vector
instructions (SVE?), but I’ve not looked into it.&lt;/p&gt;

&lt;h2 id=&quot;can-i-test-this-out-without-hardware&quot;&gt;Can I test this out without hardware?&lt;/h2&gt;

&lt;p&gt;Yes, qemu has both RISC-V support and support for its vector instructions. I
didn’t make precise notes when I tried this out months ago, but
&lt;a href=&quot;https://www.reddit.com/r/RISCV/comments/184cusi/running_qemu_with_support_for_riscv_vector/&quot;&gt;this&lt;/a&gt;
should get you started.&lt;/p&gt;

&lt;h2 id=&quot;tldr&quot;&gt;tl;dr&lt;/h2&gt;

&lt;p&gt;Vector instructions are great. I wasn’t aware of this register grouping, and I
love it.&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://llvm.org/devmtg/2019-04/slides/TechTalk-Kruppe-Espasa-RISC-V_Vectors_and_LLVM.pdf&quot;&gt;An old by interesting LLVM deck from 2019&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 24 Mar 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/03/Exploring-RISC-V-vector-instructions.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/03/Exploring-RISC-V-vector-instructions.html</guid>
        
        
        <category>programming</category>
        
        <category>risc-v</category>
        
      </item>
    
      <item>
        <title>Rebuilding FRR with pim6d</title>
        <description>&lt;p&gt;Short post today.&lt;/p&gt;

&lt;p&gt;Turns out that Debian, in its infinite wisdom, disables &lt;code class=&quot;highlighter-rouge&quot;&gt;pim6d&lt;/code&gt; in &lt;code class=&quot;highlighter-rouge&quot;&gt;frr&lt;/code&gt;. Here’s
a short howto on how to build it fixed.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt build-dep frr
[…]
$ apt source frr
[…]
$ cd frr-8*
$ DEB_BUILD_PROFILES=pkg.frr.pim6d dpkg-buildpackage -us -uc -b
$ sudo dpkg -i ../frr_*.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then you can enable pim6d in &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/frr/daemons&lt;/code&gt; and restart frr.&lt;/p&gt;

&lt;p&gt;Not that I managed to get IPv6 multicast routing to to work over wireguard
interfaces anyway. Not sure what’s wrong. Though it didn’t fix it, here’s an
interesting command that made stuff like &lt;code class=&quot;highlighter-rouge&quot;&gt;ip -6 mroute&lt;/code&gt; look like it &lt;em&gt;should&lt;/em&gt;
work:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo smcroutectl  add LAN ff38:40:fd11:222:3333:44:0:1122 wg-foo
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/02/Rebuilding-FRR-with-pim6d.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/02/Rebuilding-FRR-with-pim6d.html</guid>
        
        
        <category>multicast</category>
        
        <category>network</category>
        
      </item>
    
  </channel>
</rss>
