<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>schelcc | blog on schelcc</title><link>https://schelcc.dev/posts/index.xml</link><description>Recent content in schelcc | blog on schelcc</description><generator>Hugo</generator><language/><lastBuildDate>Wed, 23 Sep 2026 18:33:53 -0400</lastBuildDate><atom:link href="https://schelcc.dev/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>C++ Thread Safety, Reinventing The Wheel, and Making My Life Easier</title><link>https://schelcc.dev/posts/20260921143401-reinventing_the_wheel-blog/</link><pubDate>foo Wed, 23 Sep 2026 18:33:53 -0400</pubDate><guid>https://schelcc.dev/posts/20260921143401-reinventing_the_wheel-blog/</guid><description>&lt;p&gt;Alternative title: &lt;a
 href="https://en.wikipedia.org/wiki/Carcinisation"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;Carcinization&lt;/a&gt;? In my C++ codebase? It&amp;rsquo;s more likely than you think.&lt;/p&gt;
&lt;p&gt;Whilst the job search continues, I thought I&amp;rsquo;d get out a quicker writeup about a lifesaver of a tool
I&amp;rsquo;ve recently implemented in my ongoing project &lt;code&gt;indy-tui&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; and how I &lt;del&gt;stumbled&lt;/del&gt; iterated my way to it.&lt;/p&gt;
&lt;h2 id="background"&gt;Background&lt;a href="#background" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;By its nature, &lt;code&gt;indy-tui&lt;/code&gt; requires keeping several threads up and running &amp;ndash; when managing an
interface, user input, and a live websocket session, one&amp;rsquo;s hand is slightly forced. Likewise,
some amount of shared data across threads is largely unavoidable. Hence, it&amp;rsquo;s time for locking.&lt;/p&gt;
&lt;p&gt;This project has been my first &amp;ldquo;real&amp;rdquo; go at an actually multithreaded application; a desire to gain
some experience with multithreading, both generally and with C++&amp;rsquo;s Standard Library, was a strong
factor in my motivation to take it on.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; As such, the git history demonstrates me walking across the
learning curve &amp;ndash; first a lot of &lt;code&gt;std::mutex&lt;/code&gt; and &lt;code&gt;std::scoped_lock&lt;/code&gt;, followed by my own ill-fated RAII
wrapper to add logging to lock acquisition&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;, a handful of &lt;code&gt;std::atomic&lt;/code&gt; misunderstandings, only
then finally landing on &lt;code&gt;std::shared_mutex&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Thankfully, almost every instance of a need for shared data in this project&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; is a &amp;ldquo;one writer, many readers&amp;rdquo; situation,
so &lt;code&gt;std::shared_mutex&lt;/code&gt; is really the best option. Until recently, I was satisfied giving each of my
classes a shared mutex &lt;em&gt;(or multiple to give load bearing components their own)&lt;/em&gt; and keeping access to
them and their related data tightly behind getters and setters. This was largely &lt;em&gt;fine&lt;/em&gt;, it
didn&amp;rsquo;t feel the best but it seemed simple.&lt;/p&gt;
&lt;p&gt;It seemed simple.&lt;/p&gt;
&lt;h3 id="i-like-dataclasses"&gt;I like dataclasses&lt;a href="#i-like-dataclasses" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;For better or worse, I quite like dataclasses. I like more semantic code what can I say.&lt;/p&gt;
&lt;p&gt;For a UI rework inching towards some adaptation&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; of MVC, I needed to pull out
the UI logic I had put inside the leaderboard. That work was in there to begin with because the
leaderboard needed to access the vector of drivers, as well as a collection of event information
&lt;em&gt;(track name, laps to go, current flag, etc.)&lt;/em&gt;, and with these being behind several shared mutexes the
UI logic needed the ability to get locks. Why have one responsibility when you could have more?&lt;/p&gt;
&lt;p&gt;I like dataclasses, so the obvious solution to me was to pull the event information out into some
&lt;code&gt;EventInfo&lt;/code&gt; struct, with fields for each entry, and then have the leaderboard hold around an instance
of it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;EventInfo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// We aren&amp;#39;t guaranteed to get the data for these fields all at the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// same time, so we populate them as we get them, hence the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// optionals
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;optional&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;string&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; event_name;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;optional&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;string&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; track_name;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;optional&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; laps_to_go;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Fine enough, but this is shared data &amp;ndash; gotta have those mutexes. But, what gets a mutex? And who is
responsible for it? Some of these fields are updated much more frequently than others, and most of
them are read each render loop; if the full struct gets one mutex, we could end up with a lot of
contention. Conversely, if we want to give each field its own, that&amp;rsquo;s both a lot of annoying
boilerplate &lt;em&gt;and&lt;/em&gt; making the usage more annoying as I&amp;rsquo;d have to litter the codebase with
mindfully-scoped lock wrappers &lt;em&gt;(or do manual locking and risk forgetting an unlock)&lt;/em&gt; &amp;ndash; this would
look something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;EventInfo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_mutex event_name_mtx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;optional&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;string&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; event_name;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_mutex track_name_mtx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;optional&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;string&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; track_name;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Use somewhere in the codebase, say to check if we have the event name yet
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Open a tighter scope so that the lock doesn&amp;#39;t live longer than it needs to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock event_name_lock(event_info.event_name_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (event_name.has_value()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// event_name_lock is alive for all of whatever&amp;#39;s in here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// and all of whatever&amp;#39;s in here too
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s not very fun. It&amp;rsquo;s even less fun when you need multiple locks at the same time.&lt;/p&gt;
&lt;p&gt;This was the point where I had justified a tangent of trying to find a remotely ergonomic design. In
an ideal world, I wanted a type &lt;em&gt;(or macro&lt;/em&gt;)&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt; which could wrap any type and:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a shared mutex&lt;/li&gt;
&lt;li&gt;Handle getting and setting in a more ergonmic way than manually acquiring
&lt;code&gt;std::shared_lock&lt;/code&gt; and/or &lt;code&gt;std::unique_lock&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If at all possible, do (2) in such a way that trying to modify the underlying object when taking a
shared lock becomes a compilation error, as well as attempting to get a unique lock on a
const reference to the wrapped object&lt;/li&gt;
&lt;li&gt;When necessary, I can still hold a lock on the object for an arbitrary amount of time &lt;em&gt;(e.g. need
to work on several objects and need all locked to do so)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Surely&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;, the shared mutex and manual use of the shared/unique locks is
perfectly viable. I just don&amp;rsquo;t like it. Also it&amp;rsquo;s my learning project and I&amp;rsquo;ll overengineer types if
I want to.&lt;/p&gt;
&lt;p&gt;With that, let the wheel reinvention commence.&lt;/p&gt;
&lt;h2 id="the-triangular-wheel"&gt;The triangular wheel&lt;a href="#the-triangular-wheel" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;First is the solution that truly missed the mark.&lt;/p&gt;
&lt;p&gt;For whatever reason I was quite stuck in OOP brain when working on this&lt;sup id="fnref:8"&gt;&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref"&gt;8&lt;/a&gt;&lt;/sup&gt;, so immediately what came to mind was a
wrapper which simply defined a getter and setter, taking a shared lock and unique lock respectively:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Omitting constructors and whatnot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LockWrapper&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; T &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock lock(mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; obj;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(T t) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock lock(mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// In reality you&amp;#39;d want to take the input as a forwarding
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// reference and std::forward it here but there are more pressing
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// matters
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; T obj;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// By qualifying this as mutable we can take a shared_lock in a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// const-qualified method
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;mutable&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_mutex mtx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A couple problems here.&lt;/p&gt;
&lt;p&gt;A glaring issue is the signature of &lt;code&gt;get()&lt;/code&gt; &amp;ndash; we incur a copy every single time we call &lt;code&gt;get()&lt;/code&gt;! This
is quite bad. Even worse, if &lt;code&gt;T&lt;/code&gt; is not copyable it&amp;rsquo;s fully incompatible with this wrapper.&lt;/p&gt;
&lt;p&gt;Another issue is what happens when you want to first assign to the wrapped value and then use it?
You might say to return a reference to the wrapper in set:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;LockWrapper&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; set(T t) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock lock(mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then you might use the two methods together:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Let some_field be a LockWrapper of some default constructible and
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// assignable type T
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;some_field.set(T()).get();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This &amp;ldquo;works&amp;rdquo;, but there is a suspiciously elephant-shaped object in the corner that is begging to be
acknowledged: we release the unique_lock and then some time later take the shared_lock. In other
words, &lt;em&gt;we are not guaranteed that &lt;code&gt;some_field&lt;/code&gt; has not changed again between &lt;code&gt;set()&lt;/code&gt; and &lt;code&gt;get()&lt;/code&gt;&lt;/em&gt;.&lt;sup id="fnref:9"&gt;&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref"&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Regardless of if I&amp;rsquo;m wrong and there is somehow a guarantee that the two calls will always directly
follow each other, I still threw this solution out &amp;ndash; the forced copying alone is a nonstarter, and
this doesn&amp;rsquo;t allow me to actually hold onto the lock for longer if needed.&lt;/p&gt;
&lt;h2 id="the-hexagonal-wheel"&gt;The hexagonal wheel&lt;a href="#the-hexagonal-wheel" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After the above failure, another component of the &amp;ldquo;right&amp;rdquo; solution became clear: the lock itself has
to be handed off alongside the data. To take advantage of the whole RAII wrapper thing, the lock
needed to be given to the caller and stored in some way. That is, the lock must be an &lt;a
 href="https://en.cppreference.com/cpp/language/value_category"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;lvalue&lt;/a&gt; at the
callsite. Also, we should be sure to &lt;em&gt;not&lt;/em&gt; copy literally everything &amp;ndash; we should be returning
references to the wrapped object.&lt;/p&gt;
&lt;p&gt;Fortunately, someone working on the standards saw this coming &amp;ndash; &lt;code&gt;std::shared_lock&lt;/code&gt; and
&lt;code&gt;std::unique_lock&lt;/code&gt; are movable. Hence, we just need to adjust the idea behind the getter and setter
before to instead get locks. In comes the trusty &lt;code&gt;std::pair&lt;/code&gt; and &lt;a
 href="https://en.cppreference.com/cpp/language/structured_binding"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;structured bindings&lt;/a&gt;&lt;sup id="fnref:10"&gt;&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref"&gt;10&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// shared and unique lock are templated on the mutex type -- they were
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// inferred before, but won&amp;#39;t be now, so we&amp;#39;ll just add a defaulted
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// template parameter to fill them in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T, &lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; Mut &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_mutex&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LockWrapper&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Again omitting constructors
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;pair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, T &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;amp;&amp;gt;&lt;/span&gt; get_const() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(mtx), obj};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;pair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, T&lt;span style="color:#f92672"&gt;&amp;amp;&amp;gt;&lt;/span&gt; get_mut() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(mtx), obj};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; T obj;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Mut mtx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, using structured bindings we can easily get both the lock and the object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;LockedWrapper&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;string&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; s &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// We can append to the string
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; [lock, ref_s] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; s.get_mut();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ref_s.append(&lt;span style="color:#e6db74"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// And we can print the string
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; [lock, ref_s] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; s.get_const();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;println(&lt;span style="color:#e6db74"&gt;&amp;#34;{}&amp;#34;&lt;/span&gt;, ref_s);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This feels &lt;em&gt;much&lt;/em&gt; better than before &amp;ndash; we aren&amp;rsquo;t copying and we can hold the lock as long as we see
fit. But, this has a bit of a semantic downside: if we want to use the wrapped object directly in an
expression &lt;em&gt;(not break it out into a statement beforehand, and add a scope to shorten the
lifetime)&lt;/em&gt; we have to call the annoying &lt;code&gt;.second&lt;/code&gt; member of the pair:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;LockedWrapper&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;string&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; s &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;s.get_mut().second.append(&lt;span style="color:#e6db74"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;println(&lt;span style="color:#e6db74"&gt;&amp;#34;{}&amp;#34;&lt;/span&gt;, ref_s.get_const().second);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This isn&amp;rsquo;t the end of the world, but it is a bit worse than I would&amp;rsquo;ve liked.&lt;/p&gt;
&lt;h2 id="i-dream-of-better--already-existing--wheels"&gt;I dream of better (already existing) wheels&lt;a href="#i-dream-of-better--already-existing--wheels" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;At this point what I had in my head as the dream solution almost mirrored Python&amp;rsquo;s decorators: I
wanted to be able to somehow &amp;ldquo;wrap&amp;rdquo; an entire type to lock before and after like you could with a
decorated function, but have that type behave almost exactly like the wrapped type &amp;ndash; I wanted the
lock to be invisible.&lt;/p&gt;
&lt;p&gt;Now is when I reached out to a friend and much better programmer&lt;sup id="fnref:11"&gt;&lt;a href="#fn:11" class="footnote-ref" role="doc-noteref"&gt;11&lt;/a&gt;&lt;/sup&gt; to see what they thought; whether this was even a
decent pattern, or if I was simply losing it. Turns out it was a bit of both &amp;ndash; this was
&lt;a
 href="https://en.wikipedia.org/wiki/Carcinisation"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;carcinization&lt;/a&gt; manifest.&lt;/p&gt;
&lt;h2 id="the-dot-dot-dot-crab-wheel"&gt;The&amp;hellip; crab wheel?&lt;a href="#the-dot-dot-dot-crab-wheel" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;So, turns out, I was just inching ever closer to exactly how Rust handles this problem. Whoops.&lt;/p&gt;
&lt;p&gt;In Rust&amp;rsquo;s &lt;code&gt;std::sync&lt;/code&gt; they provide &lt;code&gt;MutexGuard&amp;lt;T&amp;gt;&lt;/code&gt; &amp;ndash; a RAII wrapper which locks an object protected by
a mutex, but acts just as &lt;code&gt;&amp;amp;T&lt;/code&gt; when dereferenced, and releases the lock when falling out of
scope. This is exactly what I&amp;rsquo;ve wanted all along, so it&amp;rsquo;s time for the final wheel.&lt;/p&gt;
&lt;p&gt;To mimic this, we need two new types:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One which wraps a (possibly const) reference to the wrapped type with a lock, but largely acts
like the wrapped type&lt;/li&gt;
&lt;li&gt;One which wraps a type with a shared mutex and spits out (1)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We&amp;rsquo;ll call these &lt;code&gt;LockPair&lt;/code&gt; and &lt;code&gt;Locked&lt;/code&gt; respectively.&lt;/p&gt;
&lt;h3 id="lockpair-t"&gt;&lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt;&lt;a href="#lockpair-t" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Translating (1) to C++ land for &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt;, we need to define &lt;code&gt;operator*()&lt;/code&gt; and &lt;code&gt;operator-&amp;gt;()&lt;/code&gt; for some
templated struct which has the templated type and a lock. When an instance of this struct is
dereferenced, we want to return a &lt;em&gt;(possibly const)&lt;/em&gt; reference to the wrapped object &amp;ndash; this is our
&lt;code&gt;operator*()&lt;/code&gt;. The specifics of &lt;code&gt;operator-&amp;gt;()&lt;/code&gt; are a bit of a tangent&lt;sup id="fnref:12"&gt;&lt;a href="#fn:12" class="footnote-ref" role="doc-noteref"&gt;12&lt;/a&gt;&lt;/sup&gt;, but here we can get away
with just returning a &lt;em&gt;(possibly const)&lt;/em&gt; pointer&lt;sup id="fnref:13"&gt;&lt;a href="#fn:13" class="footnote-ref" role="doc-noteref"&gt;13&lt;/a&gt;&lt;/sup&gt; to the wrapped
object.&lt;/p&gt;
&lt;p&gt;The other half of the &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt; is the lock itself. With &lt;code&gt;std::unique_lock&lt;/code&gt; and &lt;code&gt;std::shared_lock&lt;/code&gt;,
the RAII functionality is such that when the object that &lt;em&gt;currently&lt;/em&gt; owns the lock goes out of scope,
the lock is released. Thus, we have to be sure to own the lock for this to work, and at construction
we&amp;rsquo;ll want to move it in. A neat result of the RAII usage is that we don&amp;rsquo;t actually have to interact
with the lock &lt;em&gt;at all&lt;/em&gt;, we just need to hold onto it. Regardless of whether we have a unique or shared
lock, any information we need to generate the appropriate methods is contained in the templated type
&amp;ndash; we can just shove the lock in a variant of the two lock types and move on.&lt;sup id="fnref:14"&gt;&lt;a href="#fn:14" class="footnote-ref" role="doc-noteref"&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The final constraint on &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt; to note is that we template around the wrapped type, not a
reference to the wrapped type, so we&amp;rsquo;ll just add a requirement on the template that &lt;code&gt;T&lt;/code&gt; is not a
reference.&lt;/p&gt;
&lt;p&gt;This gives us &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T, &lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; ...Ts&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;concept&lt;/span&gt; IsOneOf &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;is_same_v&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T, Ts&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; ...);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// This is just a requirement that the input templated type is present
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// in a list of types. If you&amp;#39;re not familiar with C++&amp;#39;s concepts,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// just read &amp;#34;IsOneOf&amp;lt;...&amp;gt; auto&amp;#34; below as &amp;#34;any type in this given list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// of types&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T, &lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; Mut &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_mutex&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;requires&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;is_reference_v&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LockPair&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;variant&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; lock;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;reference_wrapper&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; val;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Technically we do end up taking the lock as a forwarded reference
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// here, but the locks are only movable so we don&amp;#39;t need to worry
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// about copying.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; S &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;remove_cv_t&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair(IsOneOf&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Mut&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;l,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; S &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;s)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; lock(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(l)), val(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;reference_wrapper&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;forward&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;S&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(s))) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Return a (possibly const) pointer to T
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; T &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;() { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;(val.get()); }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Return a (possibly const) ref to T
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; T &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;() { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; val.get(); }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="locked-t"&gt;&lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt;&lt;a href="#locked-t" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; has one job: control access to the contained object. Where &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt; acts as a way to
get to the object, &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; acts as a way to get to a &lt;code&gt;LockedPair&amp;lt;T&amp;gt;&lt;/code&gt;. Thus, we need to accomplish
three main items:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hold &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Define a way to retrieve a mutable reference to the object&lt;/li&gt;
&lt;li&gt;Define a way to retrieve a constant reference to the object&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For (1) we just need to concern ourselves with construction. If &lt;code&gt;T&lt;/code&gt; is default-constructible, we want
&lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; to be as well, so we&amp;rsquo;ll define that and let it be deleted whenever &lt;code&gt;T&lt;/code&gt; is not
default-constructible. In that case, we&amp;rsquo;ll have &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; be initialized with some &lt;code&gt;T&lt;/code&gt;, and once again
we&amp;rsquo;ll see the forwarding reference so that we can both copy and move into &lt;code&gt;Locked&lt;/code&gt;. Those are the
options for constructing &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; &amp;ndash; once we have &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; we only allow access to the wrapped
object through the &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt; interfaces. That is to say, there is no way to get to the
contained object directly through &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For (2) and (3), we&amp;rsquo;ll define &lt;code&gt;get_mut() -&amp;gt; LockPair&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;get_const() -&amp;gt; LockPair&amp;lt;T const&amp;gt;&lt;/code&gt; which
simply return &lt;code&gt;{std::unique_lock(_mtx), _val}&lt;/code&gt; and &lt;code&gt;{std::shared_lock(_mtx), _val}&lt;/code&gt; respectively,
passing them to &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt;&amp;rsquo;s relevant constructor. Once again, we instruct &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt; on what
kind of methods to generate &lt;em&gt;(whether the accessors are const or not)&lt;/em&gt; based on the template
parameter &lt;code&gt;T&lt;/code&gt;, not the lock we give it. One special case we&amp;rsquo;ll want to handle is when &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt;
itself is accessed through a const reference, say by accessing a class&amp;rsquo;s member &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; in the
body of a const-qualified method. In this case, we&amp;rsquo;ll want to make attempting to get mutable
access a compilation error &amp;ndash; we&amp;rsquo;ll just delete &lt;code&gt;get_mut()&lt;/code&gt; for the const-qualified overload.&lt;/p&gt;
&lt;p&gt;One final note on &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; is that allowing &lt;code&gt;T&lt;/code&gt; to be a pointer doesn&amp;rsquo;t really make sense, at least
as far as I can imagine. For my use I don&amp;rsquo;t want to permit it to hold a pointer, so I&amp;rsquo;ll add a
constraint to the template requiring this. There&amp;rsquo;s not a named constraint in &lt;code&gt;&amp;lt;type_traits&amp;gt;&lt;/code&gt; for smart
pointers, and &lt;code&gt;std::is_pointer&amp;lt;T&amp;gt;&lt;/code&gt; does not evaluate &lt;code&gt;true&lt;/code&gt; for them, so I have &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; specialized
for &lt;code&gt;std::shared_ptr&lt;/code&gt; and &lt;code&gt;std::unique_ptr&lt;/code&gt; with all methods deleted.&lt;/p&gt;
&lt;p&gt;Thus, we have &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;requires&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;is_pointer_v&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Locked&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;mutable&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_mutex _mtx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; T _val;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Permit a default ctor only if T has one
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Locked()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;requires&lt;/span&gt;(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;is_default_constructible_v&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; _val() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; U &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;remove_cv_t&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Locked(U &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;u) &lt;span style="color:#f92672"&gt;:&lt;/span&gt; _val(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;forward&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;U&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(u)) {};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_mut() { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock(_mtx), _val}; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_mut() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_const() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock(_mtx), _val}; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/* Render smart pointer instantiations unusable */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Locked&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Locked() &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; U &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;remove_cv_t&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Locked(U &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;u) &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_mut() &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_mut() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_const() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Locked&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Locked() &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;template&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; U &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;remove_cv_t&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Locked(U &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;u) &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_mut() &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_mut() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; get_const() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="i-promise-it-rolls"&gt;I promise it rolls&lt;a href="#i-promise-it-rolls" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;To wrap this up, I&amp;rsquo;ll just give two quick examples of this in use; one as part of an expression
where we don&amp;rsquo;t want a longer living lock, and the other where we do.&lt;/p&gt;
&lt;p&gt;For the first, I&amp;rsquo;ll pull out a piece of &lt;code&gt;indy-tui&lt;/code&gt;&amp;rsquo;s UI where I display some information about the on
track event. I want to show how much of the current event is left to go &amp;ndash; if the current on track
session is a practice or qualifying session, all that matters is the time left in the
event. Conversely, if it&amp;rsquo;s a race, we should show the number of laps completed alongside the total number
of laps. To accomplish this, I need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Determine the current session type, stored as a &lt;code&gt;Locked&amp;lt;std::optional&amp;lt;Telemetry::SessionType&amp;gt;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If the session type is a race:
&lt;ul&gt;
&lt;li&gt;Retrieve the current laps completed, giving us &lt;code&gt;std::optional&amp;lt;int&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;int&lt;/code&gt; is present, convert it to a string, otherwise do nothing&lt;/li&gt;
&lt;li&gt;Retrieve the total laps, giving us &lt;code&gt;std::optional&amp;lt;int&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Again convert to a string if present, otherwise do nothing&lt;/li&gt;
&lt;li&gt;Update the row as &lt;code&gt;&amp;quot;Lap&amp;quot;&lt;/code&gt; followed by &lt;code&gt;&amp;quot;{} / {}&amp;quot;&lt;/code&gt; formatted with the strings resulting from the
above steps or &lt;code&gt;&amp;quot;--&amp;quot;&lt;/code&gt; if not present&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If the session type is a practice or qualifying session:
&lt;ul&gt;
&lt;li&gt;Retrieve the time left in the session, stored as &lt;code&gt;Locked&amp;lt;std::optional&amp;lt;std::string&amp;gt;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Update the row as &lt;code&gt;&amp;quot;Time left&amp;quot;&lt;/code&gt; followed by the time left if present, otherwise &lt;code&gt;&amp;quot;--:--:--&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To do this before would&amp;rsquo;ve required manually taking a shared lock of &lt;code&gt;completed_laps&lt;/code&gt; and &lt;code&gt;total_laps&lt;/code&gt;,
possibly requiring two extra scopes if I wanted to avoid locking both at the same time&lt;sup id="fnref:15"&gt;&lt;a href="#fn:15" class="footnote-ref" role="doc-noteref"&gt;15&lt;/a&gt;&lt;/sup&gt;. The &lt;code&gt;Table&lt;/code&gt; UI element
that&amp;rsquo;s in use here has the ability to update a full row or column with a vector, and the
&lt;code&gt;std::optional&amp;lt;int&amp;gt;&lt;/code&gt; fields give us the ability to use the mapping methods &lt;code&gt;.transform()&lt;/code&gt; and
&lt;code&gt;.and_then()&lt;/code&gt; to do something with the (possibly) contained value and return another
&lt;code&gt;std::optional&amp;lt;T&amp;gt;&lt;/code&gt;. Thus, if we had the ability to safely and cleanly access these fields as part of
an expression, we could keep this row update concise. Thankfully, I have my trusty new &lt;em&gt;(slightly
oxidized&lt;/em&gt;) wheel:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (sess.session_type.get_const()&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;value_or(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SessionType&lt;span style="color:#f92672"&gt;::&lt;/span&gt;PRACTICE) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; SessionType&lt;span style="color:#f92672"&gt;::&lt;/span&gt;RACE)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res &lt;span style="color:#f92672"&gt;=&lt;/span&gt; table.update_row(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, {&lt;span style="color:#e6db74"&gt;&amp;#34;Lap&amp;#34;&lt;/span&gt;, std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;{} / {}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sess.completed_laps.get_const()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;transform([](&lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; i) { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;to_string(i); })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .value_or(&lt;span style="color:#e6db74"&gt;&amp;#34;--&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sess.total_laps.get_const()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;transform([](&lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; i) { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;to_string(i); })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .value_or(&lt;span style="color:#e6db74"&gt;&amp;#34;--&amp;#34;&lt;/span&gt;))});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res &lt;span style="color:#f92672"&gt;=&lt;/span&gt; table.update_row(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, {&lt;span style="color:#e6db74"&gt;&amp;#34;Time left&amp;#34;&lt;/span&gt;, sess.time_to_go.get_const()&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;value_or(&lt;span style="color:#e6db74"&gt;&amp;#34;--:--:--&amp;#34;&lt;/span&gt;)});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For that second example, I&amp;rsquo;ll pick out a smaller component&lt;sup id="fnref:16"&gt;&lt;a href="#fn:16" class="footnote-ref" role="doc-noteref"&gt;16&lt;/a&gt;&lt;/sup&gt;. In the logic to display the live leaderboard, I have a table of possibly changing size
dependent on two things &amp;ndash; the number of drivers, and the number of columns desired&lt;sup id="fnref:17"&gt;&lt;a href="#fn:17" class="footnote-ref" role="doc-noteref"&gt;17&lt;/a&gt;&lt;/sup&gt;. Thus, when I want to update one of these values, I
need to do some reconfiguration afterwards.&lt;/p&gt;
&lt;p&gt;For some reason, I decided to have setters for the number of drivers and the columns individually,
pulling the reconfiguration out to another method which both call. Multiple operations are done
assuming all properties are unchanged throughout the reconfiguration, so all will need to be locked
in some way. These don&amp;rsquo;t change much, so it wasn&amp;rsquo;t too much of an issue to grab a unique lock on
each element &lt;em&gt;(the table, the vector of columns, and the number of drivers&lt;/em&gt;). As discussed earlier
though, I needed to be sure we&amp;rsquo;re not releasing the lock on the newly changed component &amp;ndash; I decided
to move the retrieved &lt;code&gt;LockPair&lt;/code&gt; into the reconfiguration call. For the other components, I didn&amp;rsquo;t
want the reconfiguration method to have to know what locks it needs to take based on the caller, so
I decided to simply have the reconfiguration method move in a &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt; for each of the three
components. This way, the caller is the arbiter of which locks are taken when, moving them all into
the reconfiguration at the end regardless.&lt;/p&gt;
&lt;p&gt;For updating the columns and the subsequent reconfiguration this gives us:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; LeaderboardView&lt;span style="color:#f92672"&gt;::&lt;/span&gt;set_columns(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;ColumnInfo&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;cs) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; locked_columns &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _cols.get_mut();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;locked_columns &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(cs);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reconstruct_table(table.get_mut(), std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(locked_columns),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _num_drivers.get_mut());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// We no longer own the lock on the columns, so even though
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// locked_columns goes out of scope, the lock is not released
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; LeaderboardView&lt;span style="color:#f92672"&gt;::&lt;/span&gt;reconstruct_table(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;UI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Table&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;locked_table,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;ColumnInfo&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;locked_columns,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;size_t&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;locked_num) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Reset dimensions, adding 1 row to account for column titles
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; locked_table&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;set_dim(Table&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Dim(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;locked_num, locked_columns&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;size()));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Set alignment based on the columns
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; [idx, c] &lt;span style="color:#f92672"&gt;:&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;views&lt;span style="color:#f92672"&gt;::&lt;/span&gt;enumerate(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;locked_columns))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; locked_table&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;column_props(idx)&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;align &lt;span style="color:#f92672"&gt;=&lt;/span&gt; c.align;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// All three locks are going to go out of scope, and we own them
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// here so each lock will be released
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;a href="#conclusion" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The material takeaway is the above &amp;ndash; &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt;. But, further than that, this whole
endeavor was a bit of an excursion. Personally, I love to take these sorts of things on. It&amp;rsquo;s mostly
simple, which means nailing the ergonomics &lt;em&gt;(and actually getting the code right)&lt;/em&gt; is that much more
important; if the thing I&amp;rsquo;m trying to smooth out winds up easier to use than the thing I make&amp;hellip; well
at least I have something to learn from.&lt;/p&gt;
&lt;p&gt;Likewise, they also give me a chance to really grow my knowledge. If I&amp;rsquo;m trying to just get a task
done, I&amp;rsquo;m going to figure out how to do it and probably move on. Conversely, if I&amp;rsquo;m trying to make
something that I plan to use generally, I have to take more time paying attention to edge cases and
what&amp;rsquo;s really going on. Something that directly preceeded this actually an attempt to marry this
concept with &lt;code&gt;std::optional&lt;/code&gt;, making some sort of &lt;code&gt;LockedOptional&lt;/code&gt;. Even though that was &lt;em&gt;very&lt;/em&gt; scrapped,
trying to hand-roll &lt;code&gt;std::optional&lt;/code&gt; was very, very interesting and was a great learning
experience&lt;sup id="fnref:18"&gt;&lt;a href="#fn:18" class="footnote-ref" role="doc-noteref"&gt;18&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;So, did I reinvent the wheel a tad bit? Maybe. But, I left with a deeper understanding of C++ than I
went in with, and now I have a tool which has already proved very helpful.&lt;/p&gt;
&lt;h2 id="epilogue-these-wheels-are-made-for-rolling"&gt;Epilogue &amp;ndash; These wheels are made for rolling&lt;a href="#epilogue-these-wheels-are-made-for-rolling" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;One final not-entirely-relevant note which is relevant enough to mention.&lt;/p&gt;
&lt;p&gt;Very quickly after finishing this implementation, I noticed &lt;code&gt;LockPair&amp;lt;T&amp;gt;&lt;/code&gt; is actually useful beyond being
just a conduit for &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt;. Elsewhere in the project, I have shared components which are a little
more complex than &lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; feels good for. In one such case, I have a frequently rearranged
vector of driver objects which themselves have a fair amount going on. Between this and existence of
implementation that I don&amp;rsquo;t see the benefit in updating, this is a shared component which still must
be manually locked.&lt;/p&gt;
&lt;p&gt;However, as part of a refactor, I needed to give a part of the UI a way to get a snapshot of this
vector, as it&amp;rsquo;s the current ordering of the drivers on track. These drivers objects are not exactly
the smallest, and this is one of the most frequently hit sections of the codebase, so I don&amp;rsquo;t want
to be copying the vector each time. Ultimately, what I wanted was a way to hand off a const
reference to this vector with an automatically handled lock.&lt;/p&gt;
&lt;p&gt;Hey wait that sounds familiar.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;LockPair&lt;/code&gt;, I can do exactly that, even though the vector and its mutex are not related to a
&lt;code&gt;Locked&amp;lt;T&amp;gt;&lt;/code&gt; object whatsoever. Thus, the call for the UI to retrieve the drivers in their on-track
ordering is roughly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ThreadSafe&lt;span style="color:#f92672"&gt;::&lt;/span&gt;LockPair&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;DriverTelemetry&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TelemetryBoard&lt;span style="color:#f92672"&gt;::&lt;/span&gt;reorder_and_get() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Order drivers by rank, then calculate gap to leader
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock lock(_driver_vec_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* ... Omitted as it&amp;#39;s not relevant ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Acquire and return a shared_lock on the drivers vec
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock(_driver_vec_mtx), _drivers};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;See &lt;a
 href="https://github.com/schelcc/indy-tui"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;the github repo&lt;/a&gt; for the project, or
&lt;a
 href="https://schelcc.dev/tags/indy-tui"
 
 
&gt;#indy-tui&lt;/a&gt; for more here.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Huge shoutout to this wonderful talk from David Olsen
&lt;a
 href="https://www.youtube.com/watch?v=8rEGu20Uw4g"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;Back To Basics: C++ Concurrency&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;A tip. If you are early on in a project and are
already in enough of a deadlock mess to think that logging lock acquisition is going to be
helpful. Maybe like, do something else? Oops.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;We need not discuss
the caveat. It is certainly not a queue. Certainly I did not try a thread-safe queue based on what I
remember from my computer organization class. Certainly.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Read: mutilation.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;I did not end up trying a macro. The world should
consider itself lucky there is a part of my brain capable of keeping me from reaching for macros and
&lt;code&gt;goto&lt;/code&gt;.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;Don&amp;rsquo;t call me Shirley.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;Fear not for later that day I
watched Casey Muratori&amp;rsquo;s &lt;a
 href="https://www.youtube.com/watch?v=wo84LFzx5nI"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;The Big OOPs&lt;/a&gt; and that concluded.&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;To
verify, I wrote up a simple compiler explorer example &amp;ndash; as far as I can tell, GCC and Clang seem to
do different things with this &lt;code&gt;set()&lt;/code&gt; then &lt;code&gt;get()&lt;/code&gt; sequence. In GCC I cannot get the two to disagree
(i.e. another &lt;code&gt;set()&lt;/code&gt; occurs in between pair &lt;code&gt;set()&lt;/code&gt; and &lt;code&gt;get()&lt;/code&gt; calls), but in Clang it seems relatively
frequent. I might investigate this further later, but here&amp;rsquo;s the link:
&lt;a
 href="https://godbolt.org/z/hqWrfPve3"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;https://godbolt.org/z/hqWrfPve3&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;I can&amp;rsquo;t find
a link to the standards discussion but someone is proposing nested structured bindings which would
be so good please I am begging.&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:11"&gt;
&lt;p&gt;Huge shoutout to Rose
please check out their page &lt;a
 href="https://ikl.sh/"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;https://ikl.sh/&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:11" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:12"&gt;
&lt;p&gt;And tangent I will. If the
&lt;code&gt;operator-&amp;gt;()&lt;/code&gt; overload returns a pointer, it behaves exactly as expected, giving us the equivalent of
&lt;code&gt;(*obj).foo()&lt;/code&gt;. But, if it returns a non-pointer, &lt;code&gt;operator-&amp;gt;()&lt;/code&gt; is then applied on &lt;em&gt;that&lt;/em&gt; value,
repeating this until a pointer is returned, which is then dereferenced as usual. This &amp;ldquo;drilling
down&amp;rdquo; behavior was (to me) surprising but I guess makes sense &amp;ndash; if we have &lt;code&gt;T** ptr&lt;/code&gt; and want to
access the object, it would be quite annoying to have to do &lt;code&gt;ptr-&amp;gt;-&amp;gt;foo()&lt;/code&gt;.&amp;#160;&lt;a href="#fnref:12" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:13"&gt;
&lt;p&gt;An earlier attempt saw me returning a pointer
whenever the wrapped object did not have the dereference operator defined, otherwise I returned a
reference to the result of dereferencing it. Because of that drill-down behavior this actually
punched straight through things like std::optional and would&amp;rsquo;ve been a nightmare&amp;hellip;&amp;#160;&lt;a href="#fnref:13" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:14"&gt;
&lt;p&gt;I&amp;rsquo;ve just now
realized that, if desired, we could further template this to allow us to define the possible lock
types, meaning we &lt;em&gt;could&lt;/em&gt; extend this past shared and unique lock if we so desired.&amp;#160;&lt;a href="#fnref:14" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15"&gt;
&lt;p&gt;Which I&amp;rsquo;d
want to do to avoid unnecessary contention as these would be updated often.&amp;#160;&lt;a href="#fnref:15" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:16"&gt;
&lt;p&gt;What&amp;rsquo;s here is already a bit
vestigial &amp;ndash; I need to update some pieces which would wind up mooting this part. But it&amp;rsquo;s a good
example.&amp;#160;&lt;a href="#fnref:16" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:17"&gt;
&lt;p&gt;The columns
are functors which produce a string given a driver&amp;rsquo;s current information, so adding a new column of
information is simply adding a new functor.&amp;#160;&lt;a href="#fnref:17" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:18"&gt;
&lt;p&gt;I have read Microsoft&amp;rsquo;s &lt;a
 href="https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;deducing this&lt;/a&gt; article so many times, but only after
recognizing that my four overloads of every method was exactly what the author was talking about did
it &lt;em&gt;really&lt;/em&gt; click.&amp;#160;&lt;a href="#fnref:18" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Delayable Telemetry Queue | Design Notes - indy-tui</title><link>https://schelcc.dev/posts/20260724115454-delayble_telemetry_queue-blog/</link><pubDate>foo Thu, 06 Aug 2026 08:23:40 -0400</pubDate><guid>https://schelcc.dev/posts/20260724115454-delayble_telemetry_queue-blog/</guid><description>&lt;p&gt;As the core impetus for my most recent &lt;em&gt;(and ongoing)&lt;/em&gt; project, I think a break down of my design and implementation of
the delay queue used in &lt;a
 href="https://schelcc.dev/tags/indy-tui"
 
 
&gt;#indy-tui&lt;/a&gt; is perfectly befitting of a first post.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll start with a little background on the project, follwed by a
little more on the problem I was trying to solve and why I&amp;rsquo;m/ taking a crack at it. Then, I&amp;rsquo;ll touch on the two main
approaches I considered, what primary goals I was trying to meet, and ultimately which of the approaches stuck. Finally,
I&amp;rsquo;ll go a little more technically in-depth on the implementation I have today.&lt;/p&gt;
&lt;p&gt;If you are a motorsports fan, the non-technical part(s) of this post should be right up your
alley. If you aren&amp;rsquo;t, fear not &amp;ndash; the aforementioned non-technical part(s) should be quite
limited. With that said, &lt;a
 href="https://www.youtube.com/watch?v=Ubt9vnioGyA"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;why not just&amp;hellip; become one&lt;/a&gt;? &lt;a
 href="https://www.youtube.com/watch?v=uKEW7cCxxjk"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;I mean it&amp;rsquo;s pretty cool&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;This is &lt;em&gt;a&lt;/em&gt; solution, but certainly not &lt;em&gt;the&lt;/em&gt; solution &amp;ndash; my hope is to illustrate why I picked this one.&lt;/p&gt;
&lt;h2 id="background"&gt;Background&lt;a href="#background" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="the-problem"&gt;The problem&lt;a href="#the-problem" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Compared to &amp;ldquo;stick and ball&amp;rdquo; sports like hockey, football, or soccer, broadcasting motorsports
presents a unique challenge: the broadcast can only ever show a very small fraction of what&amp;rsquo;s happening at any given
time. Aside from the primary challenge that it&amp;rsquo;s just not possible to show all 25+ cars spanning miles on screen at
once, each of those cars has a team plotting strategy &lt;em&gt;(sometimes)&lt;/em&gt; on the radio throughout the duration of the
event. Needless to say, if your favorite driver is not in the top X or is clearly charging through the field, it&amp;rsquo;s not a
given they&amp;rsquo;ll get much coverage (if any).&lt;/p&gt;
&lt;p&gt;To their credit, various broadcasts have made progress trying to tackle this problem. The most important of these
spectator assist devices is inarguably the on screen leaderboard &amp;ndash; a live graphic which displays the state of the race
(flag status, laps completed/laps to go) and, at minimum, the current position of each driver in the field. When used
more effectively, the broadcaster will include more live information on the leaderboard, data points like each driver&amp;rsquo;s
gap to the leader, the number of laps since they last pitted, or the tire compound they&amp;rsquo;re currently equipped with.&lt;/p&gt;
&lt;p&gt;For the more observant viewer, the leaderboard goes a long way in filling the gaps inherent to motorsports
coverage. But, for the more impatiently observant viewer&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;, this presents a new pain point
in the broadcast&amp;rsquo;s production: &lt;em&gt;rarely&lt;/em&gt; is the leaderboard showing the information you would like to know at any given
time. Thankfully, today most premier motorsports series provide a much more thorough live leaderboard separate from the
broadcast, often by app, which displays most of the information at once. With this alternative, said impatiently
observant viewers&lt;sup id="fnref1:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; should be satisfied &amp;ndash; they have all of the information when they want it, even during
commercials. So&amp;hellip; they are satisfied right? Right?&lt;/p&gt;
&lt;p&gt;Have you ever had an upstairs neighbor who somehow has a slightly faster stream of the football game that you both are
watching? Watched a pass thrown and had all suspense robbed from you as said neighbor is celebrating the touchdown when,
for you, the ball is still in the air? Unfortunately, unless you are lucky enough to somehow&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;
have a broadcast with next to zero delay, this upstairs neighbor and these online leaderboard options are one in the
same &amp;ndash; the online leaderboards have a very neglible delay, while broadcasts do not. Using what the series gives you,
your two options are to either just take the on screen leaderboard and accept the lack of spoilers over getting the
information you want, or to use their online leaderboard and get the information desired but spoilers along with it.&lt;/p&gt;
&lt;p&gt;All that&amp;rsquo;s really needed here is a way to delay the leaderboard information &lt;em&gt;(from here on &amp;ldquo;telemetry&amp;rdquo;)&lt;/em&gt; by a configurable
amount. While I don&amp;rsquo;t know of any major series which offers this directly, there is third party tooling which does&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;. Although the options I&amp;rsquo;ve tried out do work, I&amp;rsquo;ve
had a few main gripes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The delay is configured in milliseconds&lt;/strong&gt;. When I&amp;rsquo;m trying to measure a delay while watching a race, I
personally don&amp;rsquo;t want to bother doing the math of adding the right number of zeros to the rough number I&amp;rsquo;ve
observed. At most, I want control down to half-seconds&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When accruing a delay, the interface stalls and doesn&amp;rsquo;t communicate whether it is broken, there is no telemetry
coming in, or it is simply building up a delay&lt;/strong&gt;. I would like a small portion of the interface to show the status of
the delay, for example how much of a delay is currently built up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The delay overshoots or undershoots over long spans&lt;/strong&gt;. I have observed times where after adjusting the delay back and
forth, it is not uncommon to wind up with the observed delay markedly longer than the configured delay.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The tools are not open source&lt;/strong&gt;. Ultimately, this is the most consequential gripe &amp;ndash; if I am the only person in the
world that has the above issues, I will happily fork the project and make it most comfortable for myself. Without
that ability, I am left to either suck it up or try my hand at a solution.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, try my hand I did.&lt;/p&gt;
&lt;h3 id="goals"&gt;Goals&lt;a href="#goals" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The delay is consistent&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;There should be strong guards against undershoot and overshoot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exact delay is more important than continuity of telemetry&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;There will be stutters in the telemetry we receive. I care that the duration between front and
back of the queue matches the desired delay much more than I can that the telemetry shown is
continuous &lt;em&gt;(w/o skipping)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The delay is configured in second increments&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;If implemented well, this is easily updated later to be half-second &lt;em&gt;(or smaller)&lt;/em&gt; increments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The queue is thread-safe&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;When used in the project, we&amp;rsquo;ll want to have what is basically a producer-consumer setup.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delay status is retrievable&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;We&amp;rsquo;ll want to show on the interface whether the delay is satisfied, and if not how much of a delay is accrued.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The delay can be zero&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;If the user wants a &amp;ldquo;true&amp;rdquo; live feed, we don&amp;rsquo;t want to have a second queue to accommodate it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="two-approaches"&gt;Two approaches&lt;a href="#two-approaches" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;When first tackling this problem, two main solutions jumped out at me &amp;ndash; a dynamic queue, and a fixed-size queue.&lt;/p&gt;
&lt;h3 id="dynamic-queue"&gt;Dynamic queue&lt;a href="#dynamic-queue" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The first, and what I suspect most other tools attempting this use, is a straightforward thread-safe queue where the
&amp;ldquo;producer&amp;rdquo; thread enqueues receieved telemetry paired with a timestamp, and the &amp;ldquo;consumer&amp;rdquo; thread dequeues telemetry
frames only if the duration between now and that frame&amp;rsquo;s timestamp satisfy the delay.&lt;/p&gt;
&lt;p&gt;This solution satisfies goals three, four, five, and partially satisfies goal one as undershooting would be
impossible. This solution also benefits from a fairly straightforward implementation, likely requiring minimal work on
top of the C++ standard library.&lt;/p&gt;
&lt;p&gt;A byproduct of this implementation is that as long as we receive a given telemetry frame, the user will
&amp;ldquo;see&amp;rdquo;&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt; every
frame. Note that I described this as a byproduct rather than a benefit &amp;ndash; this actually violates goal two. It is
unavoidable that the telemetry we receive will be at an inconsistent rate, so by ensuring this continuity we run the
risk of a delay overshoot &lt;em&gt;(thus also violating the other half of goal one)&lt;/em&gt;. With this solution, we end up implementing a
&amp;ldquo;best case&amp;rdquo; delay; if we receive telemetry faster than we can process and display it, we will quickly build up a surplus
of unprocessed telemetry frames, resulting in a delay overshoot.&lt;/p&gt;
&lt;p&gt;If the end goal were a data collection tool for later analysis of race telemetry &lt;em&gt;(which wouldn&amp;rsquo;t require delay
functionality)&lt;/em&gt; this continuity would likely be paramount, and this solution would certainly win out. But, that&amp;rsquo;s not our
application, so we need another plan.&lt;/p&gt;
&lt;h3 id="fixed-size-queue"&gt;Fixed-size queue&lt;a href="#fixed-size-queue" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;This fixed-sized queue leverages two truths in this specific application: TUI users don&amp;rsquo;t expect the utmost temporal
resolution for data presentation, and only so much data is actually understandable to humans at once. That is to
say, in this application, there is quickly a limit to how fast the interface&amp;rsquo;s refresh rate actually needs to be. Thus,
we can fix a refresh rate, for now at 10 Hz.&lt;/p&gt;
&lt;p&gt;Given a fixed refresh rate and a configured delay, we can fix the size of the queue in total delay
frames required as the refresh rate multiplied by the desired delay. Then, we allow dequeue only
when all frames are filled, and we allow enqueue only when not all frames are filled. Alongside
that, the number of frames between the front and back gives the total delay currently accrued.&lt;/p&gt;
&lt;p&gt;As long as we know that we can always process and display an individual frame faster than the
refresh&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;, we know that the delay is consistent as per
goal one. Similarly, by only allowing enqueue when the delay is not satisfied, we know that at any
given time the frame at the back of the queue is a snapshot in time exactly as long ago as the delay
is configured, regardless of whether there were received frames between the last &amp;ldquo;snapshot&amp;rdquo; and the
most recent one, satisfying goal two. The &amp;ldquo;fixed&amp;rdquo; queue size is using the configured delay, so
regardless of the current choice we can recalculate it as the configuration changes, satisfying goal
three. As you will see, the implementation is thread safe, and as noted above we can easily
calculate the current status of the delay, knocking out goals four and five.&lt;/p&gt;
&lt;p&gt;This leaves us with goal six, and there&amp;rsquo;s an option here to feed two birds with one scone&lt;sup id="fnref:8"&gt;&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref"&gt;8&lt;/a&gt;&lt;/sup&gt;. See, one problem with this proposed solution is a
sort of &amp;ldquo;hitching&amp;rdquo; problem &amp;ndash; there&amp;rsquo;s this back-and-forth dance where the queue is full, the
telemetry frame at the front is dequeued and shown, and before a new frame is received the interface
tries and now fails to dequeue the next frame. To get around this, we can simply add some &amp;ldquo;slop&amp;rdquo; to
the end of the queue, some relatively small number of frames with which we permit the queue to &lt;em&gt;just
barely&lt;/em&gt; over/undershoot. This way, the interface has a handful of delay-satisfactory frames to chew
on while waiting for new telemetry to come in, and the telemetry receiver thread has some padding to
flex in case a burst of frames come in.&lt;/p&gt;
&lt;p&gt;Now we feed the second bird: when we want to go &amp;ldquo;live&amp;rdquo;, we drop the queue down to just the &amp;ldquo;slop&amp;rdquo;
frames. With a refresh rate of 10 Hz &lt;em&gt;(that&amp;rsquo;s 10 telemetry frames every second)&lt;/em&gt; it would take ten
slop frames to reach a delay of one second. So, as long as we keep the slop frames to at most around
five, we can keep the &amp;ldquo;live&amp;rdquo; delay down to less than half a second. This does reintroduce the
hitching problem, but with a truly live feed the hitching is unavoidable as again the telemetry is
coming in at a varying rate. As far as I have observed in testing, this solution is perfectly
satisfactory for a live display.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all six goals &amp;ndash; on to implementation.&lt;/p&gt;
&lt;h2 id="implementation"&gt;Implementation&lt;a href="#implementation" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note: This will reflect what is live in the repo at the time of writing, but I do not guarantee that it will remain
that way.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="the-delayinfo-dataclass"&gt;The &lt;code&gt;DelayInfo&lt;/code&gt; dataclass&lt;a href="#the-delayinfo-dataclass" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Whenever either &lt;code&gt;delay_s&lt;/code&gt; &lt;em&gt;(delay in seconds)&lt;/em&gt; or &lt;code&gt;refresh_hz&lt;/code&gt; &lt;em&gt;(refresh rate in hz)&lt;/em&gt;, two calculated parameters change:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;total_frames&lt;/code&gt; : The calculated number of frames in the queue&lt;/li&gt;
&lt;li&gt;&lt;code&gt;frame_period&lt;/code&gt; : The amount of time represented by one frame&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Given how tightly bound these four parameters are, and how frequently they&amp;rsquo;re used together throughout the
implementation, it&amp;rsquo;s important for thread safety that we prohibit the use of one whilst any other is changing. This is
all &lt;code&gt;DelayInfo&lt;/code&gt; exists to do.&lt;/p&gt;
&lt;p&gt;Omitting some not-so-relevant details, we lay out the class as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DelayInfo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; size_t DELAY_SLOP &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size_t _delay_s &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size_t _refresh_hz &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_mutex _mtx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;recalculate_params&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/** @brief Instantiate the delayinfo with the configured parameters. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DelayInfo(size_t delay_s, size_t refresh_hz)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; _delay_s(delay_s), _refresh_hz(refresh_hz) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; recalculate_params();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;set_delay_s&lt;/span&gt;(size_t &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;set_refresh_hz&lt;/span&gt;(size_t &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;[[nodiscard]]&lt;/span&gt; size_t get_delay_s();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;[[nodiscard]]&lt;/span&gt; size_t get_refresh_hz();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;[[nodiscard]]&lt;/span&gt; size_t get_delay_frames();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;[[nodiscard]]&lt;/span&gt; Time&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Duration&lt;span style="color:#f92672"&gt;::&lt;/span&gt;DblMilliSec get_frame_period();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The most important detail here is the &lt;code&gt;shared_mutex&lt;/code&gt;. Since &lt;code&gt;recalculate_params&lt;/code&gt; will need to change
multiple paramaters, we need to guard against any getters/setters accessing any parameters during
recalculation. But, since the getters don&amp;rsquo;t need to change any state and thus don&amp;rsquo;t need to block
other getters, we simply take a &lt;code&gt;unique_lock&lt;/code&gt; of &lt;code&gt;_mtx&lt;/code&gt; in the setters and &lt;code&gt;recalculate_params&lt;/code&gt;, and take
a &lt;code&gt;shared_lock&lt;/code&gt; in the getters. Given how relatively infrequently the delay configuration should be
changing, &lt;code&gt;_mtx&lt;/code&gt; should rarely be contested.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll skip going over the implementation of the getters and setters, as &lt;code&gt;recalculate_params&lt;/code&gt; should
give a good idea:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;recalculate&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Called by any setters
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock lock(_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _total_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (_delay_s &lt;span style="color:#f92672"&gt;*&lt;/span&gt; _refresh_hz) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; DELAY_SLOP;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _frame_period &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Time&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Duration&lt;span style="color:#f92672"&gt;::&lt;/span&gt;DblMilliSec(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1000.0&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static_cast&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(_refresh_hz));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take the &lt;code&gt;unique_lock&lt;/code&gt; as mentioned before&lt;/li&gt;
&lt;li&gt;Calculate the number of frames needed as the delay multiplied by the refresh rate, adding the slop
frames&lt;/li&gt;
&lt;li&gt;Calculate the frame period &lt;em&gt;(total amount of time represented by one frame, in milliseconds)&lt;/em&gt; as the
reciprocal of the refresh rate&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="telemetryqueue"&gt;&lt;code&gt;TelemetryQueue&lt;/code&gt;&lt;a href="#telemetryqueue" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Rather than just walk through the class definition, we&amp;rsquo;ll approach it from a more bottom-up
direction. To start with, we&amp;rsquo;ll go over some background on the queue and then the most important two
functions of this &lt;em&gt;(or any)&lt;/em&gt; queue: enqueueing and dequeueing. Following that, we&amp;rsquo;ll close out by
covering the queue reconfiguration process.&lt;/p&gt;
&lt;h4 id="queue-background"&gt;Queue background&lt;a href="#queue-background" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Since the number of the frames in the queue at any time is essentially fixed-size, I opted to reach
for a single-ended &lt;a
 href="https://en.wikipedia.org/wiki/Circular_buffer"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;circular queue&lt;/a&gt;. With this approach, we simply track monotonically increasing
enqueue and dequeue indices, adjusting them only when we take in or give out a telemetry
frame. Then, to access the queue, the actual position is calculated as the index reduced modulo the
queue size.&lt;/p&gt;
&lt;p&gt;As highlighted earlier, a primary goal was to be able to split the &amp;ldquo;producer&amp;rdquo; and &amp;ldquo;consumer&amp;rdquo; logic
between different threads; one to receive telemetry, the other to process and display it. With this
design, we just need two small additions to get there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A mutex on each individual frame, so that we don&amp;rsquo;t try to access the same frame in both threads
&lt;em&gt;(possible if the queue is either empty or full)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;A mutex on the entire queue, so that we can lock all frames during resize &lt;em&gt;(more on this shortly)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One final piece to note is my error handling strategy. Whenever possible, I prefer to reach for
&amp;ldquo;errors as values&amp;rdquo; as opposed to exceptions, and I do this for two primary reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Forcing the caller to acknowledge the existence of and do something with errors makes code more
readable at the callsite &lt;em&gt;(in my opinion)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Requiring erroneous paths to return meaningful information forces me to more carefully consider
what should be an error and often leads me to write more thorough code with stronger guarantees.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach is what I chose for most of the error handling in this project. Here, I have an error
type &lt;code&gt;TelemetryQueue::Err&lt;/code&gt; which has various states representing errors we might expect to see. Here,
the two most relevant states are &lt;code&gt;Err::TOO_RECENT&lt;/code&gt; and &lt;code&gt;Err::DELAY_FULL&lt;/code&gt;, noting respectively that there
is currently not enough accrued delay to dequeue a frame or that there is currently exactly enough
accrued delay and to enqueue a new frame would cause the delay to overshoot.&lt;/p&gt;
&lt;p&gt;With that, we&amp;rsquo;ll now walk through the implementation.&lt;/p&gt;
&lt;h4 id="dequeue"&gt;Dequeue&lt;a href="#dequeue" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;To start with, we see the aforementioned errors-as-values approach. Here, we leverage C++23&amp;rsquo;s
wonderful &lt;a
 href="https://en.cppreference.com/cpp/utility/expected"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;std::expected&lt;/a&gt;, where we note that we&amp;rsquo;ll return either a &lt;code&gt;unique_ptr&lt;/code&gt; representing a
telemetry frame, or an &lt;code&gt;Err&lt;/code&gt; representing the error encountered. Also, since we are handling errors
using &lt;code&gt;std::expected&lt;/code&gt;, we will not throw any exceptions and anything we call will not either. Thus, we
can mark the method as &lt;code&gt;noexcept&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;expected&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;TelemetryFrame&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Err&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;dequeue() &lt;span style="color:#66d9ef"&gt;noexcept&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, we double check that the dequeue index is not ahead of the enqueue index, as this is
considered &amp;ldquo;impossible&amp;rdquo; &amp;ndash; if this has happened, we are in an unrecoverrable state. As such, we
check this with an assertion&lt;sup id="fnref:9"&gt;&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref"&gt;9&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assert(_deq_idx &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; _enq_idx);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we check that the delay is satisfied. The call to &lt;code&gt;delay_info&lt;/code&gt;&amp;rsquo;s &lt;code&gt;get_min_delay_frames()&lt;/code&gt; just
retrieves the minimum number of frames between the enqueue and dequeue indices such that the delay
is satisfied, which is just the total number of frames before the addition of the slop frames. If
the delay is satisified, we continue on, and if not, we log a debug message and return the
corresponding error state&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((_enq_idx &lt;span style="color:#f92672"&gt;-&lt;/span&gt; _deq_idx) &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; _delay_info.get_min_delay_frames()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... what follows goes here ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tools&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Log&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Debug(&lt;span style="color:#e6db74"&gt;&amp;#34;Dequeue skipped, delay is not ready&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;TELEM-QUEUE&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unexpected(Err{Err&lt;span style="color:#f92672"&gt;::&lt;/span&gt;TOO_RECENT});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Continuing assuming the delay is satisfied, we get a &lt;code&gt;shared_lock&lt;/code&gt; on the full queue and then again
check against an &amp;ldquo;impossible&amp;rdquo; state to ensure the queue is not empty. The mutex on the full queue
will only be contested if we try to resize the queue, a relatively rare occurence. Every step
following assumes that the queue has not been resized, so we acquire the &lt;code&gt;shared_lock&lt;/code&gt; &lt;em&gt;(using a &lt;a
 href="https://en.cppreference.com/cpp/language/raii"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;RAII&lt;/a&gt;
wrapper, so &lt;code&gt;.lock()&lt;/code&gt; is called at construction and &lt;code&gt;.unlock()&lt;/code&gt; at destruction)&lt;/em&gt; at the start and do not
release until the &lt;code&gt;true&lt;/code&gt; body exits. Since this is a &lt;code&gt;shared_lock&lt;/code&gt;, both enqueue and dequeue can acquire
it simultaneously.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock full_lock(_frame_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assert(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;_frames.empty());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we index the full queue at the dequeue index reduced modulo the queue size, and acquire a lock
on its mutex &lt;em&gt;(this is again a RAII wrapper, just not shared lock this time)&lt;/em&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;cur_frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _frames.at(_deq_idx &lt;span style="color:#f92672"&gt;%&lt;/span&gt; _frames.size());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;scoped_lock single_lock(cur_frame.mtx);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we check if the frame retrieved is actually present. Given that we return frames as a
&lt;code&gt;std::unique_ptr&amp;lt;TelemetryFrame&amp;gt;&lt;/code&gt;, when we give them to the caller we are moving&lt;sup id="fnref:10"&gt;&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref"&gt;10&lt;/a&gt;&lt;/sup&gt; them out of the queue. Then,
since &lt;code&gt;std::unique_ptr&lt;/code&gt;&amp;rsquo;s move constructor sets the moved-from ptr to &lt;code&gt;nullptr&lt;/code&gt;, a frame which is not
yet populated will be &lt;code&gt;nullptr&lt;/code&gt;. As such, if the frame we retrieve is &lt;code&gt;nullptr&lt;/code&gt;, it means that we have
somehow advanced to a frame which is not ready, so the delay is not yet satisied. This should
generally not happen, but it is good practice to check pointers regardless.&lt;/p&gt;
&lt;p&gt;The use of &lt;code&gt;std::unique_ptr&lt;/code&gt; for handing around the frames is a vestige of prior unrelated troubleshooting
regarding the &lt;code&gt;TelemetryFrame&lt;/code&gt;&amp;rsquo;s move constructor. At this point it&amp;rsquo;s not necessary, and is probably a
target for future refactor/cleanup. With that said, the indirection cost of a pointer when accessing
the frame is negligible here, so to change it would be only for cleanliness &lt;em&gt;(still a very worthy cause)&lt;/em&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (cur_frame.frame &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unexpected(Err{Err&lt;span style="color:#f92672"&gt;::&lt;/span&gt;TOO_RECENT});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we know that we have a valid frame. As such, we&amp;rsquo;ll step the dequeue index forward one frame
and then return the frame we retrieved before.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_deq_idx&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(cur_frame.frame);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now for the full dequeue logic:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;expected&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;TelemetryFrame&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Err&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;dequeue() &lt;span style="color:#66d9ef"&gt;noexcept&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assert(_deq_idx &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; _enq_idx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((_enq_idx &lt;span style="color:#f92672"&gt;-&lt;/span&gt; _deq_idx) &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; _delay_info.get_min_delay_frames()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock full_lock(_frame_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assert(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;_frames.empty());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;cur_frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _frames.at(_deq_idx &lt;span style="color:#f92672"&gt;%&lt;/span&gt; _frames.size());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;scoped_lock single_lock(cur_frame.mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (cur_frame.frame &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unexpected(Err{Err&lt;span style="color:#f92672"&gt;::&lt;/span&gt;TOO_RECENT});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _deq_idx&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(cur_frame.frame);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tools&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Log&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Debug(&lt;span style="color:#e6db74"&gt;&amp;#34;Dequeue skipped, delay is not ready&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;TELEM-QUEUE&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unexpected(Err{Err&lt;span style="color:#f92672"&gt;::&lt;/span&gt;TOO_RECENT});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="enqueue"&gt;Enqueue&lt;a href="#enqueue" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Since the enqueue logic mirrors that of dequeue, we&amp;rsquo;ll start with the full method and dissect where
they differ.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;expected&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;, TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Err&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;enqueue(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;string_view &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; payload) &lt;span style="color:#66d9ef"&gt;noexcept&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assert(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;payload.empty());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assert(_deq_idx &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; _enq_idx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((_enq_idx &lt;span style="color:#f92672"&gt;-&lt;/span&gt; _deq_idx) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; _delay_info.get_delay_frames()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;shared_lock full_lock(_frame_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assert(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;_frames.empty());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;cur_frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _frames.at(_enq_idx &lt;span style="color:#f92672"&gt;%&lt;/span&gt; _frames.size());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;scoped_lock single_lock(cur_frame.mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cur_frame.frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;make_unique&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;TelemetryFrame&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(Tools&lt;span style="color:#f92672"&gt;::&lt;/span&gt;b64_decode(payload));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _enq_idx&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; cur_frame.frame&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;is_valid()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;expected&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;, TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Err&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unexpected(Err{Err&lt;span style="color:#f92672"&gt;::&lt;/span&gt;FRAME_INVALID});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tools&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Log&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Debug(&lt;span style="color:#e6db74"&gt;&amp;#34;Enqueue skipped, delay is full&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;TELEM-QUEUE&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unexpected(Err{Err&lt;span style="color:#f92672"&gt;::&lt;/span&gt;DELAY_FULL});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Firstly, here we take in a &lt;a
 href="https://en.cppreference.com/cpp/string/basic_string_view"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;string_view&lt;/a&gt; of the received base64-encoded telemetry payload. We only need
to read the string we take in, and that string&amp;rsquo;s lifetime will extend beyond this called function,
so we use a &lt;code&gt;string_view&lt;/code&gt; and save on an unnecessary copy. We&amp;rsquo;ll also double check that payload is
non-empty with an assertion, as this should not be possible.&lt;/p&gt;
&lt;p&gt;The second difference is in the delay satisfaction check &amp;ndash; this is the negation of the one in
&lt;code&gt;dequeue&lt;/code&gt;, thus we only permit enqueueing a new frame if there are fewer frames than the delay
requires between the front and back.&lt;/p&gt;
&lt;p&gt;Next, rather than retrieve a frame we must create one. We do this by first getting a reference
to the current slot in the queue, and then construct a frame by passing in the base64-decoded payload.&lt;/p&gt;
&lt;p&gt;The final difference is in the return: if we succeed, we don&amp;rsquo;t need to return anything, and if we
don&amp;rsquo;t we need to tell the caller why. Hence, once we&amp;rsquo;ve built the frame and set the current slot in
the queue accordingly, we need to check if the frame is in a valid state. If it is, we return
nothing, and if not, we return the previously mentioned &lt;code&gt;Err&lt;/code&gt; with the corresponding state.&lt;/p&gt;
&lt;h4 id="queue-reconfiguration"&gt;Queue reconfiguration&lt;a href="#queue-reconfiguration" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Here is where we&amp;rsquo;ll handle rebuilding the delay upon delay reconfiguration. If the delay is
decreased, we need to shrink the queue size, and likewise grow it if the delay is increased.&lt;/p&gt;
&lt;p&gt;It would be a very frustrating user experience if the full delay had to be re-accrued upon every
delay change, so we need to preserve whatever amount of the delay is relevant when rebuilding
the delay &amp;ndash; we will come back to this shortly.&lt;/p&gt;
&lt;p&gt;As mentioned before, delay reconfiguration is the only time the full queue&amp;rsquo;s mutex needs to block
enqueue/dequeue &lt;em&gt;(and enqueue/dequeue needs to block reconfiguration)&lt;/em&gt;, so we&amp;rsquo;ll start by taking a
&lt;code&gt;unique_lock&lt;/code&gt; on the full queue mutex and a &lt;code&gt;scoped_lock&lt;/code&gt; on the &lt;code&gt;DelayInfo&lt;/code&gt;&amp;rsquo;s mutex.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;rebuild_delay() &lt;span style="color:#66d9ef"&gt;noexcept&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock frame_lock(_frame_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;scoped_lock lock(_delay_info_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we calculate the new queue size, and then how many frames forward we need to skip.
If the delay is increasing, the frame at the front of the queue &lt;em&gt;(next to be dequeued)&lt;/em&gt; is now too
recent, and thus no skip is necessary. If the delay is decreasing, the front of the queue is
now too old, requiring us to skip some number of frames ahead.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;size_t prev_delay_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _frames.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;size_t new_delay_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _delay_info.get_delay_frames();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;size_t skip_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (new_delay_frames &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; prev_delay_frames)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; ((prev_delay_frames &lt;span style="color:#f92672"&gt;-&lt;/span&gt; new_delay_frames) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since C++&amp;rsquo;s &lt;code&gt;std::mutex&lt;/code&gt; is neither copyable nor movable, and the queue contains a wrapped version of
&lt;code&gt;TelemetryFrame&lt;/code&gt; adding a mutex, the &lt;code&gt;std::vector&lt;/code&gt; which underlies the queue is also neither copyable
nor movable. Hence, we cannot simply extend the existing vector &amp;ndash; we instead create a new one and
move preserved frames over.&lt;/p&gt;
&lt;p&gt;So, first we create the new vector with the new length:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;LockedFrame&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; new_frames(new_delay_frames);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, if this is the initial configuration of the queue, there are no frames to preserve, and we can
just set our vector and return early:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (_enq_idx &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(new_frames);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we move the dequeue index forward by the number of skip frames calculated before. When the
delay is rapidly increased and decreased, it is sometimes possible to end up with the skip frames
pushing the dequeue index ahead of the enqueue index. Since the dequeue index should always be less
than or equal to the enqueue index, we&amp;rsquo;ll set it to the minimum of the two:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_deq_idx &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; skip_frames;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_deq_idx.store(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;min(_deq_idx, _enq_idx));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we&amp;rsquo;ll copy over the frames we need to preserve, if any. Starting at the dequeue index, we&amp;rsquo;ll
simply move each frame over until we reach the enqueue index:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (prev_delay_frames &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (size_t copy_idx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _deq_idx; copy_idx &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; _enq_idx; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;copy_idx) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; new_frames.at(copy_idx &lt;span style="color:#f92672"&gt;%&lt;/span&gt; new_delay_frames).frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(_frames.at(copy_idx &lt;span style="color:#f92672"&gt;%&lt;/span&gt; prev_delay_frames).frame);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we move the new vector into &lt;code&gt;_frames&lt;/code&gt; and are done:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(new_frames);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In total, for queue reconfiguration we have:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; TelemetryQueue&lt;span style="color:#f92672"&gt;::&lt;/span&gt;rebuild_delay() &lt;span style="color:#66d9ef"&gt;noexcept&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_lock frame_lock(_frame_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;scoped_lock lock(_delay_info_mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size_t prev_delay_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _frames.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size_t new_delay_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _delay_info.get_delay_frames().value_or(DelayInfo&lt;span style="color:#f92672"&gt;::&lt;/span&gt;DELAY_SLOP);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size_t skip_frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (new_delay_frames &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; prev_delay_frames)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; ((prev_delay_frames &lt;span style="color:#f92672"&gt;-&lt;/span&gt; new_delay_frames) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;LockedFrame&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; new_frames(new_delay_frames);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (_enq_idx &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(new_frames);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _deq_idx &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; skip_frames;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _deq_idx.store(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;min(_deq_idx, _enq_idx));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (prev_delay_frames &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (size_t copy_idx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _deq_idx; copy_idx &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; _enq_idx; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;copy_idx) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; new_frames.at(copy_idx &lt;span style="color:#f92672"&gt;%&lt;/span&gt; new_delay_frames).frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(_frames.at(copy_idx &lt;span style="color:#f92672"&gt;%&lt;/span&gt; prev_delay_frames).frame);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _frames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(new_frames);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;a href="#conclusion" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There is much, much more to talk about regarding this project; implementing a delay was merely the
main motivating factor in me starting it. The design I originally pictured and what is live on
&lt;a
 href="https://github.com/schelcc/indy-tui"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;GitHub&lt;/a&gt; certainly differ, but not by &lt;em&gt;that&lt;/em&gt; much. Even so, there were countless different iterations at
what you see here, all of which will forever remain as commits quickly undone.&lt;/p&gt;
&lt;p&gt;I started with something resembling what you see here, albeit significantly less &amp;ldquo;developed&amp;rdquo; and
explainable. With many tweaks and corrections, I was eventually able to get that initial design to
&lt;em&gt;(mostly)&lt;/em&gt; work. But, as I&amp;rsquo;m sure many can relate to, I convinced myself that wasn&amp;rsquo;t the &amp;ldquo;right&amp;rdquo; way
to tackle this problem. Somewhere along the way I had convinced myself that I was &amp;ldquo;cheating&amp;rdquo;, that
this was a hacky solution &amp;ndash; I had myself convinced that if I wanted to do it &amp;ldquo;right&amp;rdquo; I needed to
implement the aforementioned &lt;a
 href="#dynamic-queue"
 
 
&gt;dynamic queue&lt;/a&gt;. I sunk a not insignificant amount of time into the
dynamic solution, got it to the point where it worked with my crude prototype of a display, and
excitedly got to testing it.&lt;/p&gt;
&lt;p&gt;It did not work.&lt;/p&gt;
&lt;p&gt;The delay was inconsistent, it wasn&amp;rsquo;t always clear whether the delay was even building
correctly. Every time I&amp;rsquo;d try to use it, I would find a different way to trick the delay into
getting out of sync. To be sure, a fair amount of these problems originated between the keyboard and
chair, but some of the failure modes I encountered were remarkably reminiscent of the ways I&amp;rsquo;ve seen
other similar tools fail.&lt;/p&gt;
&lt;p&gt;Whether any subset of the issues I noted using the dynamic solution were inherent to that solution or
not, I had to make a call: stick with what I had convinced myself to be the &amp;ldquo;right&amp;rdquo; solution which
I could not seem to make work, or go back to what I had come up with on my first pass. Put more
bluntly, I needed to decide whether to get in line with what I had convinced myself &amp;ldquo;everyone else&amp;rdquo;
was doing, or to trust my design intuition.&lt;/p&gt;
&lt;p&gt;As now explained, I went with the latter. After much consideration, I&amp;rsquo;d decided that I could
more effectively defend the solution seen here than the dynamic solution I had laid out. I
went back and once again implemented yet another telemetry queue, now taking a little more time to
make sure every decision was grounded in a set of end goals. What resulted was definitely rough
around the edges and took some effort to smooth it out &amp;ndash; that&amp;rsquo;s unavoidable. But, with that effort,
I eventually got to a point where I finally &lt;em&gt;couldn&amp;rsquo;t&lt;/em&gt; trick the delay into failing. Surely, there are
latent issues which will show themselves in due time, but what I have now is something that meets
the goals I laid out, built following a design process I executed.&lt;/p&gt;
&lt;p&gt;Once again: this is &lt;em&gt;a&lt;/em&gt; solution, certainly not &lt;em&gt;the&lt;/em&gt; solution. Hopefully, it&amp;rsquo;s now clear why I&amp;rsquo;ve picked
this one.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;yeah this was
lowkey track limits but it&amp;rsquo;s still awesome&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;it&amp;rsquo;s me I am said viewer&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href="#fnref1:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;this is a really cool
explanation of this delay problem and why it&amp;rsquo;s not going anywhere &lt;a
 href="https://www.youtube.com/watch?v=CgcXli8NxHw"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;https://www.youtube.com/watch?v=CgcXli8NxHw&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a
 href="https://multiviewer.app"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;multiviewer&lt;/a&gt; is an excellent tool and works across many series&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Also, your observed broadcast delay fluctuates over time &amp;ndash;
if you want calibrate your delay to less than 0.5s/500ms you&amp;rsquo;re likely going to have to spend a lot of time
monitoring and readjusting it. I personally would like to just set my telemetry delay to be &lt;em&gt;just&lt;/em&gt; longer than my
broadcast delay and accept that what I see on my leaderboard is just barely behind what I see on TV.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;telemetry is sent rather quickly, with each frame representing at most slightly less than a second&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;and, we can &amp;ndash; we pick the refresh rate&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;this
has, for better or worse, worked its way into my lexicon:
&lt;a
 href="https://x.com/peta/status/1070066047414345729"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;https://x.com/peta/status/1070066047414345729&lt;/a&gt;&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;I cannot wait to rewrite this and make this a
&lt;a
 href="https://en.cppreference.com/cpp/language/contracts"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;pre and post condition&lt;/a&gt; once C++26 is mature&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;here is a decent
explainer on those new to C++&amp;rsquo;s move semantics:
&lt;a
 href="https://stackoverflow.com/questions/3106110/what-is-move-semantics"
 
 
 class="link--external" target="_blank" rel="noreferrer"
 
&gt;https://stackoverflow.com/questions/3106110/what-is-move-semantics&lt;/a&gt;&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item></channel></rss>