<!doctype html><htmllang=endir=ltrclass="blog-wrapper blog-post-page plugin-blog plugin-id-default"data-has-hydrated=false><metacharset=UTF-8><metaname=generatorcontent="Docusaurus v3.6.1"><titledata-rh=true>Captain's Cookbook: Practical usage | The Old Speice Guy</title><metadata-rh=truename=viewportcontent="width=device-width,initial-scale=1.0"><metadata-rh=truename=twitter:cardcontent=summary_large_image><metadata-rh=trueproperty=og:urlcontent=https://speice.io/2018/01/captains-cookbook-part-2><metadata-rh=trueproperty=og:localecontent=en><metadata-rh=truename=docusaurus_localecontent=en><metadata-rh=truename=docusaurus_tagcontent=default><metadata-rh=truename=docsearch:languagecontent=en><metadata-rh=truename=docsearch:docusaurus_tagcontent=default><metadata-rh=trueproperty=og:titlecontent="Captain's Cookbook: Practical usage | The Old Speice Guy"><metadata-rh=truename=descriptioncontent="A look at more practical usages of Cap'N Proto"><metadata-rh=trueproperty=og:descriptioncontent="A look at more practical usages of Cap'N Proto"><metadata-rh=trueproperty=og:typecontent=article><metadata-rh=trueproperty=article:published_timecontent=2018-01-16T13:00:00.000Z><linkdata-rh=truerel=iconhref=/img/favicon.ico><linkdata-rh=truerel=canonicalhref=https://speice.io/2018/01/captains-cookbook-part-2><linkdata-rh=truerel=alternatehref=https://speice.io/2018/01/captains-cookbook-part-2hreflang=en><linkdata-rh=truerel=alternatehref=https://speice.io/2018/01/captains-cookbook-part-2hreflang=x-default><scriptdata-rh=truetype=application/ld+json>{"@context":"https://schema.org","@id":"https://speice.io/2018/01/captains-cookbook-part-2","@type":"BlogPosting","author":{"@type":"Person","name":"Bradlee Speice"},"dateModified":"2024-11-10T01:23:31.000Z","datePublished":"2018-01-16T13:00:00.000Z","description":"A look at more practical usages of Cap'N Proto","headline":"Captain's Cookbook: Practical usage","isPartOf":{"@id":"https://speice.io/","@type":"Blog","name":"Blog"},"keywords":[],"mainEntityOfPage":"https://speice.io/2018/01/captains-cookbook-part-2","name":"Captain's Cookbook: Practical usage","url":"https://speice.io/2018/01/captains-cookbook-part-2"}</script><linkrel=alternatetype=application/rss+xmlhref=/rss.xmltitle="The Old Speice Guy RSS Feed"><linkrel=alternatetype=application/atom+xmlhref=/atom.xmltitle="The Old Speice Guy Atom Feed"><linkrel=stylesheethref=/katex/katex.min.css><linkrel=stylesheethref=/assets/css/styles.16c3428d.css><scriptsrc=/assets/js/runtime~main.29a27dcf.jsdefer></script><scriptsrc=/assets/js/main.d461af80.jsdefer></script><bodyclass=navigation-with-keyboard><script>!function(){vart,e=function(){try{returnnewURLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{returnwindow.localStorage.getItem("theme")}catch(t){}}();t=null!==e?e:"light",document.documentElement.setAttribute("data-theme",t)}(),function(){try{for(var[t,e]ofnewURLSearchParams(window.location.search).entries())if(t.startsWith("docusaurus-data-")){vara=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><divid=__docusaurus><divrole=regionaria-label="Skip to main content"><aclass=skipToContent_fXgnhref=#__docusaurus_skipToContent_fallback>Skip to main content</a></div><navaria-label=Mainclass="navbar navbar--fixed-top"><divclass=navbar__inner><divclass=navbar__items><buttonaria-label="Toggle navigation bar"aria-expanded=falseclass="navbar__toggle clean-btn"type=button><svgwidth=30height=30viewBox="0 0 30 30"aria-hidden=true><pathstroke=currentColorstroke-linecap=roundstroke-miterlimit=10stroke-width=2d="M4 7h22M4 15h22M4 23h22"/></svg></button><aclass=navbar__brandhref=/><divclass=navbar__logo><imgsrc=/img/logo.svgalt="Sierpinski Gasket"class="themedComponent_mlkZ themedComponent--light_NVdE"><imgsrc=/img/logo-dark.svgalt="Sierpinski Gasket"class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><bclass="navbar__titletext--
<p><ahref=/2018/01/captains-cookbook-part-1>Part 1</a> of this series took a look at a basic starting project
with Cap'N Proto. In this section, we're going to take the (admittedly basic) schema and look at how we can add a pretty
basic feature - sending Cap'N Proto messages between threads. It's nothing complex, but I want to make sure that there's
some documentation surrounding practical usage of the library.</p>
<p>As a quick refresher, we build a Cap'N Proto message and go through the serialization/deserialization steps
<ahref=https://github.com/bspeice/capnp_cookbook_1/blob/master/src/main.rstarget=_blankrel="noopener noreferrer">here</a>. Our current example is going to build on
the code we wrote there; after the deserialization step, we'll try and send the <code>point_reader</code> to a separate thread
for verification.</p>
<p>I'm going to walk through the attempts as I made them and my thinking throughout.
If you want to skip to the final project, check out the code available <ahref=https://github.com/bspeice/capnp_cookbook_2target=_blankrel="noopener noreferrer">here</a></p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id=attempt-1-move-the-reference>Attempt 1: Move the reference<ahref=#attempt-1-move-the-referenceclass=hash-linkaria-label="Direct link to Attempt 1: Move the reference"title="Direct link to Attempt 1: Move the reference"></a></h2>
<p>As a first attempt, we're going to try and let Rust move the reference. Our code will look something like:</p>
<p>Well, the Rust compiler doesn't really like this. We get four distinct errors back:</p>
<divclass="codeBlockContainer_Ckt0 theme-code-block"style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><divclass=codeBlockContent_biex><pretabindex=0class="prism-code language-text codeBlock_bY9V thin-scrollbar"style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><codeclass=codeBlockLines_e6Vv><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">error[E0277]: the trait bound `*const u8: std::marker::Send` is not satisfied in `[closure@src/main.rs:31:37: 36:6 point_reader:point_capnp::point::Reader<'_>]` </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> --> src/main.rs:31:18 </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">31 | let handle = std::thread::spawn(move || { </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"style=display:inline-block></span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">error[E0277]: the trait bound `*const capnp::private::layout::WirePointer: std::marker::Send` is not satisfied in `[closure@src/main.rs:31:37: 36:6 point_reader:point_capnp::point::Reader<'_>]` </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> --> src/main.rs:31:18 </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">31 | let handle = std::thread::spawn(move || { </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const capnp::private::layout::WirePointer` cannot be sent between threads safely </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"style=display:inline-block></span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">error[E0277]: the trait bound `capnp::private::arena::ReaderArena: std::marker::Sync` is not satisfied </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> --> src/main.rs:31:18 </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">31 | let handle = std::thread::spawn(move || { </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | ^^^^^^^^^^^^^^^^^^ `capnp::private::arena::ReaderArena` cannot be shared between threads safely
<p>Note, I've removed the help text for brevity, but suffice to say that these errors are intimidating.
Pay attention to the text that keeps on getting repeated though: <code>XYZ cannot be sent between threads safely</code>.</p>
<p>This is a bit frustrating: we own the <code>buffer</code> from which all the content was derived, and we don't have any
unsafe accesses in our code. We guarantee that we wait for the child thread to stop first, so there's no possibility
of the pointer becoming invalid because the original thread exits before the child thread does. So why is Rust
preventing us from doing something that really should be legal?</p>
<p>This is what is known as <ahref=https://doc.rust-lang.org/1.8.0/book/references-and-borrowing.htmltarget=_blankrel="noopener noreferrer">fighting the borrow checker</a>.
Let our crusade begin.</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id=attempt-2-put-the-reader-in-a-box>Attempt 2: Put the <code>Reader</code> in a <code>Box</code><ahref=#attempt-2-put-the-reader-in-a-boxclass=hash-linkaria-label="Direct link to attempt-2-put-the-reader-in-a-box"title="Direct link to attempt-2-put-the-reader-in-a-box"></a></h2>
<p>The <ahref=https://doc.rust-lang.org/std/boxed/struct.Box.htmltarget=_blankrel="noopener noreferrer"><code>Box</code></a> type allows us to convert a pointer we have
(in our case the <code>point_reader</code>) into an "owned" value, which should be easier to send across threads.
<p>Spoiler alert: still doesn't work. Same errors still show up.</p>
<divclass="codeBlockContainer_Ckt0 theme-code-block"style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><divclass=codeBlockContent_biex><pretabindex=0class="prism-code language-text codeBlock_bY9V thin-scrollbar"style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><codeclass=codeBlockLines_e6Vv><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">error[E0277]: the trait bound `*const u8: std::marker::Send` is not satisfied in `point_capnp::point::Reader<'_>` </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> --> src/main.rs:33:18 </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">33 | let handle = std::thread::spawn(move || { </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"style=display:inline-block></span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">error[E0277]: the trait bound `*const capnp::private::layout::WirePointer: std::marker::Send` is not satisfied in `point_capnp::point::Reader<'_>` </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> --> src/main.rs:33:18 </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">33 | let handle = std::thread::spawn(move || { </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const capnp::private::layout::WirePointer` cannot be sent between threads safely </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"style=display:inline-block></span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">error[E0277]: the trait bound `capnp::private::arena::ReaderArena: std::marker::Sync` is not satisfied </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> --> src/main.rs:33:18 </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain">33 | let handle = std::thread::spawn(move || { </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> | ^^^^^^^^^^^^^^^^^^ `capnp::private::arena::ReaderArena` cannot be shared between threads safely </span><br></span><spanclass=token-linestyle="color:hsl(230, 8%, 24%)"><spanclass="token plain"> |
<p>Let's be a little bit smarter about the exceptions this time though. What is that
<ahref=https://doc.rust-lang.org/std/marker/trait.Send.htmltarget=_blankrel="noopener noreferrer"><code>std::marker::Send</code></a> thing the compiler keeps telling us about?</p>
<p>The documentation is pretty clear; <code>Send</code> is used to denote:</p>
<blockquote>
<p>Types that can be transferred across thread boundaries.</p>
</blockquote>
<p>In our case, we are seeing the error messages for two reasons:</p>
<ol>
<li>
<p>Pointers (<code>*const u8</code>) are not safe to send across thread boundaries. While we're nice in our code
making sure that we wait on the child thread to finish before closing down, the Rust compiler can't make
that assumption, and so complains that we're not using this in a safe manner.</p>
</li>
<li>
<p>The <code>point_capnp::point::Reader</code> type is itself not safe to send across threads because it doesn't
implement the <code>Send</code> trait. Which is to say, the things that make up a <code>Reader</code> are themselves not thread-safe,
so the <code>Reader</code> is also not thread-safe.</p>
</li>
</ol>
<p>So, how are we to actually transfer a parsed Cap'N Proto message between threads?</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id=attempt-3-the-typedreader>Attempt 3: The <code>TypedReader</code><ahref=#attempt-3-the-typedreaderclass=hash-linkaria-label="Direct link to attempt-3-the-typedreader"title="Direct link to attempt-3-the-typedreader"></a></h2>
<p>The <code>TypedReader</code> is a new API implemented in the Cap'N Proto <ahref=https://crates.io/crates/capnp/0.8.14target=_blankrel="noopener noreferrer">Rust code</a>.
We're interested in it here for two reasons:</p>
<ol>
<li>
<p>It allows us to define an object where the <em>object</em> owns the underlying data. In previous attempts,
the current context owned the data, but the <code>Reader</code> itself had no such control.</p>
</li>
<li>
<p>We can compose the <code>TypedReader</code> using objects that are safe to <code>Send</code> across threads, guaranteeing
that we can transfer parsed messages across threads.</p>
</li>
</ol>
<p>The actual type info for the <ahref=https://github.com/capnproto/capnproto-rust/blob/f0efc35d7e9bd8f97ca4fdeb7c57fd7ea348e303/src/message.rs#L181target=_blankrel="noopener noreferrer"><code>TypedReader</code></a>
is a bit complex. And to be honest, I'm still really not sure what the whole point of the
<ahref=https://doc.rust-lang.org/std/marker/struct.PhantomData.htmltarget=_blankrel="noopener noreferrer"><code>PhantomData</code></a> thing is either.
My impression is that it lets us enforce type safety when we know what the underlying Cap'N Proto
message represents. That is, technically the only thing we're storing is the untyped binary message;
<code>PhantomData</code> just enforces the principle that the binary represents some specific object that has been parsed.</p>
<p>Either way, we can carefully construct something which is safe to move between threads:</p>
<p>And while we've left Rust to do the dirty work of actually moving the <code>point_reader</code> into the new thread,
we could also use things like <ahref=https://doc.rust-lang.org/std/sync/mpsc/index.htmltarget=_blankrel="noopener noreferrer"><code>mpsc</code> channels</a> to achieve a similar effect.</p>