<?xml version="1.0" encoding="UTF-8" standalone="yes"?><oembed><version><![CDATA[1.0]]></version><provider_name><![CDATA[The ryg blog]]></provider_name><provider_url><![CDATA[https://fgiesen.wordpress.com]]></provider_url><author_name><![CDATA[fgiesen]]></author_name><author_url><![CDATA[https://fgiesen.wordpress.com/author/fgiesen/]]></author_url><title><![CDATA[The Magic Ring&nbsp;Buffer]]></title><type><![CDATA[link]]></type><html><![CDATA[<p>This is a cute little trick that would be way more useful if there was proper OS support for it. It&#8217;s useful in a fairly small number of cases, but it&#8217;s nice enough to be worth writing up.</p>
<h3>Ring buffers</h3>
<p>I&#8217;m assuming you know what a ring buffer is, and how it&#8217;s typically implemented. I&#8217;ve <a href="https://fgiesen.wordpress.com/2010/12/14/ring-buffers-and-queues/">written about it before</a>, focusing on different ways to keep count of the fill state (and what that means for the invariants). Ring buffers are a really nice data structure &#8211; the only problem is that everything that directly interfaces with ring buffers needs to be aware of the fact, to handle wrap-around correctly. With careful interface design, this can be done fairly transparently: if you use the technique I described in <a href="https://fgiesen.wordpress.com/2011/11/21/buffer-centric-io/">&#8220;Buffer-centric IO&#8221;</a>, the producer has enough control over the consumer&#8217;s view on the data to make the wrap-around fully transparent. However, while this is a nice way of avoiding copies in the innards of an IO system, it&#8217;s too unwieldy to pass down to client code. A lot of code is written assuming data that is completely linear in memory, and using such code with ring buffers requires copying the data out to a linear block in memory first.</p>
<p>Unless you&#8217;re using what I&#8217;m calling a &#8220;magic ring buffer&#8221;, that is.</p>
<h3>The idea</h3>
<p>The underlying concept is quite simple: &#8220;unwrap&#8221; the ring by placing multiple identical copies of it right next to each other in memory. In theory you can have an arbitrary number of copies, but in practice two is sufficient in basically all practical use cases, so that&#8217;s what I&#8217;ll be using here.</p>
<p>Of course, keeping the multiple copies in sync is both annoying and tricky to get right, and essentially doing every memory access twice is a bad idea. Luckily, we don&#8217;t actually need to keep two physical copies of the data around. Really the only thing we need is to have the same physical data in two separate (logical, or virtual) memory locations &#8211; and virtual memory (paging) hardware is, at this point, ubiquitous. Using paging means we get some external constraints: buffers have to have sizes that are (at least) a multiple of the processor&#8217;s page size (potentially larger, if virtual memory is managed at a coarser granularity) and meet some alignment constraints. If those restrictions are acceptable, then really all we need is some way to get the OS to map the same physical memory multiple times into our address space, using calls available from user space.</p>
<p>The right facility turns out to be memory mapped files: modern OSes generally provide support for anonymous mmaps, which are &#8220;memory mapped files&#8221; that aren&#8217;t actually backed by any file at all (except maybe the swap file). And using the right incantations, these OSes can indeed be made to map the same region of physical memory multiple times into a contiguous virtual address range &#8211; exactly what we need!</p>
<h3>The code</h3>
<p>I&#8217;ve written a basic implementation of this idea in C++ for Windows. The code is available <a href="https://gist.github.com/3158316">here</a>. The implementation is a bit dodgy (see comments) since Windows won&#8217;t let me reserve a memory region for memory-mapping (as far as I can tell, this can only be done for allocations), so there&#8217;s a bit of a song and dance routine involved in trying to get an address we can map to &#8211; other threads might be end up allocating the memory range we just found between us freeing it and completing our own mapping, so we might have to retry several times.</p>
<p>On the various Unix flavors, you can try the same basic principle, though you might actually need to create a backing file in some cases (I don&#8217;t see a way to do it without when relying purely on POSIX functionality). For Linux you should be able to do it using an anonymous shared mmap followed by remap_file_pages. No matter which Unix flavor you&#8217;re on, you can do it without a race condition, so that part is much nicer. (Though it&#8217;s really better without a backing file, or at most a backing file on a RAM disk, since you certainly don&#8217;t want to cause disk IO with this).</p>
<p>The code also has a small example to show it in action.</p>
<p><b>UPDATE</b>: In the comments there is now also a link to two articles describing how to implement the same idea (and it turns out using pretty much the same trick) on MacOS X, and it turns out that <a href="http://en.wikipedia.org/wiki/Circular_buffer#Optimized_POSIX_Implementation">Wikipedia</a> has working code for a (race-condition free) POSIX variant as described earlier.</p>
<h3>Coda: Why I originally wanted this</h3>
<p>There&#8217;s a few cases where this kind of thing is useful (several of them IO-related, as mentioned in the introduction), but the case where I originally really wanted this was for inter-thread communication. The setting had one thread producing variable-sized commands and one thread consuming them, with a SPSC queue inbetween them. Without a magic ring buffer, doing this was a major hassle: wraparound could theoretically happen anywhere in the middle of a command (well, at any word boundary anyway), so this case was detected and there was a special command to skip ahead in the ring buffer that was inserted whenever the &#8220;real&#8221; command would&#8217;ve wrapped around. With a magic ring buffer, all the logic and special cases just disappear, and so does some amount of wasted memory. It&#8217;s not huge, but it sure is nice.</p>
]]></html></oembed>