mirror of
https://github.com/bspeice/speice.io
synced 2024-12-23 00:58:09 -05:00
75 lines
76 KiB
HTML
75 lines
76 KiB
HTML
|
<!doctype html><html lang=en dir=ltr class="blog-wrapper blog-post-page plugin-blog plugin-id-default" data-has-hydrated=false><meta charset=UTF-8><meta name=generator content="Docusaurus v3.6.0"><title data-rh=true>Captain's Cookbook: Practical usage | The Old Speice Guy</title><meta data-rh=true name=viewport content="width=device-width,initial-scale=1.0"><meta data-rh=true name=twitter:card content=summary_large_image><meta data-rh=true property=og:url content=https://speice.io/2018/01/captains-cookbook-part-2><meta data-rh=true property=og:locale content=en><meta data-rh=true name=docusaurus_locale content=en><meta data-rh=true name=docusaurus_tag content=default><meta data-rh=true name=docsearch:language content=en><meta data-rh=true name=docsearch:docusaurus_tag content=default><meta data-rh=true property=og:title content="Captain's Cookbook: Practical usage | The Old Speice Guy"><meta data-rh=true name=description content="A look at more practical usages of Cap'N Proto"><meta data-rh=true property=og:description content="A look at more practical usages of Cap'N Proto"><meta data-rh=true property=og:type content=article><meta data-rh=true property=article:published_time content=2018-01-16T13:00:00.000Z><link data-rh=true rel=icon href=/img/favicon.ico><link data-rh=true rel=canonical href=https://speice.io/2018/01/captains-cookbook-part-2><link data-rh=true rel=alternate href=https://speice.io/2018/01/captains-cookbook-part-2 hreflang=en><link data-rh=true rel=alternate href=https://speice.io/2018/01/captains-cookbook-part-2 hreflang=x-default><script data-rh=true type=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><link rel=alternate type=application/rss+xml href=/rss.xml title="The Old Speice Guy RSS Feed"><link rel=alternate type=application/atom+xml href=/atom.xml title="The Old Speice Guy Atom Feed"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css integrity=sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM crossorigin><link rel=stylesheet href=/assets/css/styles.ae6ff4a3.css><script src=/assets/js/runtime~main.751b419d.js defer></script><script src=/assets/js/main.62ce6156.js defer></script><body class=navigation-with-keyboard><script>!function(){var t,e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();t=null!==e?e:"light",document.documentElement.setAttribute("data-theme",t)}(),function(){try{for(var[t,e]of new URLSearchParams(window.location.search).entries())if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id=__docusaurus><div role=region aria-label="Skip to main content"><a class=skipToContent_fXgn href=#__docusaurus_skipToContent_fallback>Skip to main content</a></div><nav aria-label=Main class="navbar navbar--fixed-top"><div class=navbar__inner><div class=navbar__items><button aria-label="Toggle navigation bar" aria-expanded=false class="navbar__toggle clean-btn" type=button><svg width=30 height=30 viewBox="0 0 30 30" aria-hidden=true><path stroke=currentColor stroke-linecap=round stroke-miterlimit=10 stroke-width=2 d="M4 7h22M4 15h22M4 23h22"/></svg></button><a class=navbar__brand href=/><div class=navbar__logo><img src=/img/logo.svg alt="Sierpinski Gasket" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src=
|
|||
|
<p><a href=/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
|
|||
|
<a href=https://github.com/bspeice/capnp_cookbook_1/blob/master/src/main.rs target=_blank rel="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 <a href=https://github.com/bspeice/capnp_cookbook_2 target=_blank rel="noopener noreferrer">here</a></p>
|
|||
|
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=attempt-1-move-the-reference>Attempt 1: Move the reference<a href=#attempt-1-move-the-reference class=hash-link aria-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>
|
|||
|
<div class="language-rust codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-rust codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token keyword" style="color:hsl(301, 63%, 40%)">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:hsl(221, 87%, 60%)">main</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// ...assume that we own a `buffer: Vec<u8>` containing the binary message content from</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// somewhere else</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">let</span><span class="token plain"> deserialized </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token namespace">capnp</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token namespace">serialize</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token function" style="color:hsl(221, 87%, 60%)">read_message</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">&</span><span class="token keyword" style="color:hsl(301, 63%, 40%)">mut</span><span class="token plain"> buffer</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">as_slice</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token namespace">capnp</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token namespace">message</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token class-name" style="color:hsl(35, 99%, 36%)">ReaderOptions</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token function" style="color:hsl(221, 87%, 60%)">new</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuat
|
|||
|
<p>Well, the Rust compiler doesn't really like this. We get four distinct errors back:</p>
|
|||
|
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="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><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> --> src/main.rs:31:18 </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">31 | let handle = std::thread::spawn(move || { </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="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><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> --> src/main.rs:31:18 </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">31 | let handle = std::thread::spawn(move || { </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const capnp::private::layout::WirePointer` cannot be sent between threads safely </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">error[E0277]: the trait bound `capnp::private::arena::ReaderArena: std::marker::Sync` is not satisfied </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> --> src/main.rs:31:18 </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">31 | let handle = std::thread::spawn(move || { </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="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 <a href=https://doc.rust-lang.org/1.8.0/book/references-and-borrowing.html target=_blank rel="noopener noreferrer">fighting the borrow checker</a>.
|
|||
|
Let our crusade begin.</p>
|
|||
|
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=attempt-2-put-the-reader-in-a-box>Attempt 2: Put the <code>Reader</code> in a <code>Box</code><a href=#attempt-2-put-the-reader-in-a-box class=hash-link aria-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 <a href=https://doc.rust-lang.org/std/boxed/struct.Box.html target=_blank rel="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.
|
|||
|
Our next attempt looks something like this:</p>
|
|||
|
<div class="language-rust codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-rust codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token keyword" style="color:hsl(301, 63%, 40%)">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:hsl(221, 87%, 60%)">main</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// ...assume that we own a `buffer: Vec<u8>` containing the binary message content</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// from somewhere else</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">let</span><span class="token plain"> deserialized </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token namespace">capnp</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token namespace">serialize</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token function" style="color:hsl(221, 87%, 60%)">read_message</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">&</span><span class="token keyword" style="color:hsl(301, 63%, 40%)">mut</span><span class="token plain"> buffer</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">as_slice</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token namespace">capnp</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token namespace">message</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token class-name" style="color:hsl(35, 99%, 36%)">ReaderOptions</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token function" style="color:hsl(221, 87%, 60%)">new</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuat
|
|||
|
<p>Spoiler alert: still doesn't work. Same errors still show up.</p>
|
|||
|
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">error[E0277]: the trait bound `*const u8: std::marker::Send` is not satisfied in `point_capnp::point::Reader<'_>` </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> --> src/main.rs:33:18 </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">33 | let handle = std::thread::spawn(move || { </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="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><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> --> src/main.rs:33:18 </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">33 | let handle = std::thread::spawn(move || { </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | ^^^^^^^^^^^^^^^^^^ `*const capnp::private::layout::WirePointer` cannot be sent between threads safely </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">error[E0277]: the trait bound `capnp::private::arena::ReaderArena: std::marker::Sync` is not satisfied </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> --> src/main.rs:33:18 </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">33 | let handle = std::thread::spawn(move || { </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> | ^^^^^^^^^^^^^^^^^^ `capnp::private::arena::ReaderArena` cannot be shared between threads safely </span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> |
|
|||
|
<p>Let's be a little bit smarter about the exceptions this time though. What is that
|
|||
|
<a href=https://doc.rust-lang.org/std/marker/trait.Send.html target=_blank rel="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>
|
|||
|
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=attempt-3-the-typedreader>Attempt 3: The <code>TypedReader</code><a href=#attempt-3-the-typedreader class=hash-link aria-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 <a href=https://crates.io/crates/capnp/0.8.14 target=_blank rel="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 <a href=https://github.com/capnproto/capnproto-rust/blob/f0efc35d7e9bd8f97ca4fdeb7c57fd7ea348e303/src/message.rs#L181 target=_blank rel="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
|
|||
|
<a href=https://doc.rust-lang.org/std/marker/struct.PhantomData.html target=_blank rel="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>
|
|||
|
<div class="language-rust codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-rust codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token keyword" style="color:hsl(301, 63%, 40%)">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:hsl(221, 87%, 60%)">main</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// ...assume that we own a `buffer: Vec<u8>` containing the binary message content from somewhere else</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">let</span><span class="token plain"> deserialized </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token namespace">capnp</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token namespace">serialize</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token function" style="color:hsl(221, 87%, 60%)">read_message</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">&</span><span class="token keyword" style="color:hsl(301, 63%, 40%)">mut</span><span class="token plain"> buffer</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">as_slice</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token namespace">capnp</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token namespace">message</span><span class="token namespace punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token class-name" style="color:hsl(35, 99%, 36%)">ReaderOptions</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">::</span><span class="token function" style="color:hsl(221, 87%, 60%)">new</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">unwrap</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class
|
|||
|
<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 <a href=https://doc.rust-lang.org/std/sync/mpsc/index.html target=_blank rel="noopener noreferrer"><code>mpsc</code> channels</a> to achieve a similar effect.</p>
|
|||
|
<p>So now we're able to define basic Cap'N Proto messages, and send them all around our programs.</div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Blog post page navigation"><a class="pagination-nav__link pagination-nav__link--prev" href=/2018/01/captains-cookbook-part-1><div class=pagination-nav__sublabel>Older post</div><div class=pagination-nav__label>Captain's Cookbook: Project setup</div></a><a class="pagination-nav__link pagination-nav__link--next" href=/2018/05/hello><div class=pagination-nav__sublabel>Newer post</div><div class=pagination-nav__label>Hello!</div></a></nav></main><div class="col col--2"><div class="tableOfContents_bqdL thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href=#attempt-1-move-the-reference class="table-of-contents__link toc-highlight">Attempt 1: Move the reference</a><li><a href=#attempt-2-put-the-reader-in-a-box class="table-of-contents__link toc-highlight">Attempt 2: Put the <code>Reader</code> in a <code>Box</code></a><li><a href=#attempt-3-the-typedreader class="table-of-contents__link toc-highlight">Attempt 3: The <code>TypedReader</code></a></ul></div></div></div></div></div><footer class=footer><div class="container container-fluid"><div class="footer__bottom text--center"><div class=footer__copyright>Copyright © 2024 Bradlee Speice</div></div></div></footer></div>
|