    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Allocator for (Re)Actors with Optional Kinda-Safety and Relocation</title>
        <link>https://members.accu.org/index.php/journals/2378</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>


        <h2>Journal Articles</h2>


<div class="xar-mod-head"><span class="xar-mod-title">Overload Journal #139 - June 2017 + Programming Topics</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/journals/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c78/">Overload</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c374/">o139</a>
                    (7)
<br />

                                            <a href="https://members.accu.org/index.php/journals/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c65/">Programming</a>
                    (877)
<br />

                                            <a href="https://members.accu.org/index.php/journals/c374-65/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/journals/c374+65/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Allocator for (Re)Actors with Optional Kinda-Safety and Relocation</h1>
<p><strong>Author:</strong>&nbsp;Martin Moene</p>
<p>
<strong>Date:</strong> 06 June 2017 13:28:19 +01:00 or Tue, 06 June 2017 13:28:19 +01:00</p>
<p><strong>Summary:</strong>&nbsp;How do you deal with memory for (Re)Actors? Sergey Ignatchenko proposes an allocation scheme.</p>
<p><strong>Body:</strong>&nbsp;<p>Disclaimer: as usual, the opinions within this article are those of â€˜No Bugsâ€™ Hare, and do not necessarily coincide with the opinions of the translators and <em>Overload</em> editors; also, please keep in mind that translation difficulties from Lapine (like those described in [<a href="[Loganberry04]">Loganberry04</a>]) might have prevented an exact translation. In addition, the translator and <em>Overload</em> expressly disclaim all responsibility from any action or inaction resulting from reading this article.</p>

<h2>What is it about</h2>

<p>As it says on the tin, this article is about allocators within the context of (Re)Actors (a.k.a. Reactors, Actors, ad hoc FSMs, Event-Driven Programs, and so on). </p>

<p>The main benefits we get from our C++ allocator (with (Re)Actors and proposed allocation model as a prerequisite), are the following:</p>

<ul>
	<li>We have the option of having the best possible performance (same as that of good olâ€™ plain C/C++)</li>
	
	<li>Without changing app-level code, we have the option of tracking access to â€˜deadâ€™ objects via â€˜danglingâ€™ pointers (causing exception rather than memory corruption in the case of such access)</li>
	
	<li>Again, without changing app-level code, we have the option of having a compactable heap. Very briefly, compacting the heap is often very important for long-running programs, as without relocation, programs are known to fall victim to so-called â€˜external fragmentationâ€™. Just one very common scenario: if we allocate a million 100-byte small objects, we will use around 25,000 4K CPU pages; then if we randomly delete 900,000 of our 100-byte objects, weâ€™ll still have around 24,600 pages in use (unable to release them back to OS), just because it so happened that each of the remaining 24,600 pages has at least one non-deleted object. Such scenarios are quite common, and tend to cause quite a bit of trouble (in the example above, weâ€™re wasting about 9x more memory than we really need, <em>plus</em> we have very poor spatial locality too, which is quite likely to waste cache space and to hurt performance).
	
		<ul>
			<li>As a side note, many garbage-collected programming languages have been using compactable heaps for ages; Iâ€™ve seen this capability to compact used as an argument that garbage-collected languages are inherently better (and an argument against C++).</li>
		</ul>	
	</li>
</ul>

<p>Letâ€™s note that while what weâ€™ll be doing allows us to achieve benefits which are <em>comparable</em> to using traditional non-C++ mark-compact garbage collectors, weâ€™re achieving those benefits in a <em>significantly different</em> manner. On the other hand, I donâ€™t want to argue whether what weâ€™re doing really qualifies as â€˜automated garbage collectionâ€™, or if the name should be different. In the form described in this article, it is not even reference-counted garbage collection (though a similar approach can be applied to allocation models based on <code>std::shared_ptr&lt;&gt; + std::weak_ptr&lt;&gt;</code> â€“ as long as weâ€™re staying within (Re)Actors).</p>

<p>What is important though, is to:</p>

<ul>
	<li>Significantly reduce chances for errors/mistakes while coding.
	
		<ul>
			<li>Within the proposed allocation model, there are no manual <code>delete</code>s, which should help quite a bit in this regard.</li>
		
			<li>In addition, the handling of â€˜danglingâ€™ pointers is expected to help quite a bit too (at least while debugging, but in some cases also in production).</li>
		</ul>
	</li>
	
	<li>Allow for best-possible performance when we need it, while allowing it to be a little bit reduced (but still good enough for most production code) if we happen to need to track some bugs (or to rely on the handling of â€˜danglingâ€™ pointers).</li>
	
	<li>Allow for a compactable heap (again, giving <em>some</em> performance hit compared to the best-possible performance â€“ but the performance hit should usually be mild enough to run our compactable heap in production).</li>
</ul>

<h2>Message-passing is the way to go</h2>

<p>Before starting to speak about memory allocation, we need to define what those (Re)Actors weâ€™re about to rely on are about (and why theyâ€™re so important).</p>

<p>For a long while, I have been a strong proponent of message-passing mechanisms over mutex-based thread sync for concurrency purposes (starting from [<a href="Ignatchenko.xml#id([NoBugs10])">NoBugs10</a>]). Fortunately, I am not alone with such a view; just as one example, the Go languageâ€™s concept of â€œ<em>Do not communicate by sharing memory; instead, share memory by communicating</em>â€ [<a href="Ignatchenko.xml#id([Go2010])">Go2010</a>] is pretty much the same thing.</p>

<p>However, only after returning from ACCU2017 â€“ and listening to a brilliant talk [<a href="Ignatchenko.xml#id([Henney17])">Henney17</a>] â€“ I realized that weâ€™re pretty much at the point of no return, and are about to reach a kinda-consensus that </p>

<p class="blockquote">
<em>Message-passing is THE way to implement concurrency at app-level</em></p>

<p>(as opposed to traditional mutex-based thread sync).</p>

<p>The reasons for this choice are numerous â€“ and range from â€œ<em>mutexes and locks are there to </em><em>prevent</em><em> concurrency</em>â€ (as it was pointed out in [<a href="Ignatchenko.xml#id([Henney17])">Henney17</a>]), to â€œ<em>doing both thread sync and app-level logic at the same time tends to exceed cognitive limits of the human brain</em>â€ [<a href="Ignatchenko.xml#id([NoBugs15] )">NoBugs15</a>].</p>

<p>For the time being, it is not clear which of the message passing mechanisms will win (and whether one single mechanism will win at all) â€“ but as I have had very good experiences with (Re)Actors (a.k.a. Actors, Reactors, ad hoc FSMs, and Event-Driven Programs), for the rest of this article I will concentrate on them. </p>

<h2>Setting</h2>

<p>To be a bit more specific, letâ€™s describe what I understand as (Re)Actors.</p>

<p>Letâ€™s use Generic Reactor as the common denominator for all our (Re)Actors. This Generic Reactor is just an abstract class, and has a pure virtual function <code>react()</code>:</p>

<pre class="programlisting">
  class GenericReactor {
    virtual void react(const Event&amp; ev) = 0;
  }</pre>

<p>Letâ€™s name any piece of code which calls GenericReactorâ€™s react() the â€˜Infrastructure Codeâ€™. Quite often, this call is within the so-called â€˜event loopâ€™:</p>

<pre class="programlisting">
  std::unique_ptr&lt;GenericReactor&gt; r 
         = reactorFactory.createReactor(...);
  while(true) {  			//event loop
    Event ev = get_event(); //from select(), libuv, ...
    r-&gt;react(ev);
  }</pre>
  
<p>Letâ€™s note that the <code>get_event()</code> function can obtain events from wherever we want â€“ from <code>select()</code> (which is quite typical for servers) to libraries such as <code>libuv</code> (which is common for clients). </p>

<p>Also letâ€™s note that an event loop, such as the one above, is by far <em>not</em> the only way to call <code>react()</code>: Iâ€™ve seen implementations of Infrastructure Code ranging from one running multiple (Re)Actors within the same thread, to another one which deserialized the (Re)Actor from a DB, then called react(), and then serialized the (Re)Actor back to the DB. Whatâ€™s important, though, is that even if <code>react()</code> can be called from different threads â€“ it MUST be called <em>as if</em> it is one single thread (=â€˜if necessary, all thread sync should be done OUTSIDE of our (Re)Actor, so <code>react()</code> doesnâ€™t need to bother about thread sync regardless of the Infrastructure Code in useâ€™).</p>

<p>Finally, letâ€™s name any specific derivative from Generic Reactor (which actually implements our <code>react()</code> function), a Specific Reactor:</p>

<pre class="programlisting">
  class SpecificReactor : public GenericReactor {
    void react(const Event&amp; ev) override;
  };</pre>
  
<p>Also, letâ€™s observe that whenever a (Re)Actor needs to communicate with another (Re)Actor â€“ adhering to the â€˜Do not communicate by sharing memory; instead, share memory by communicatingâ€™ principle â€“ it merely sends a message, and it is only this message which will be shared between (Re)Actors.</p>

<h2>Trivial optimization: single-threaded allocator</h2>

<p>Armed with (Re)Actors, we can easily think of a very simple optimization for our allocation techniques. As all the processing within (Re)Actors is single-threaded, we can easily say that:</p>

<ul>
	<li>(Re)Actor allocators can be single-threaded (i.e. without any thread sync â€“ and avoiding relatively expensive â€˜compare-and-swapâ€™ operations).
	
	<ul>
		<li>One exception to this is those messages which the (Re)Actor sends to the others â€“ but classes implementing those messages can easily use a different (thread-synced) allocator.</li>
	</ul>
	
	</li>
	
	<li>For the purposes of this article, weâ€™ll say that each (Re)Actor will have its own private (and single-threaded) heap. While this approach can be generalized to per-thread heaps (which may be different from per-(Re)Actor heaps, in cases of multiple (Re)Actors per thread) we wonâ€™t do that here.</li>
</ul>

<p>Ok, letâ€™s write it down that our (Re)Actor allocator is single-threaded â€“ and weâ€™ll rely on this fact for the rest of this article (and everybody who has written a multi-threaded allocator will acknowledge that writing a single-threaded one is a big relief). </p>

<p>However, weâ€™ll go MUCH further than this rather trivial observation.</p>

<h2>Allocation model: owning refs, soft refs, naked refs</h2>

<p>At this point, we need to note that in C++ (as mentioned, for example, in [<a href="Ignatchenko.xml#id([Sutter11])">Sutter11</a>]), it is impossible to provide compacted heaps â€œ<em>without at least a new pointer type</em>â€. Now, letâ€™s see what can be done about it.</p>

<p>Letâ€™s consider how we handle memory allocations within our (Re)Actor. Letâ€™s say that <em>within</em> our (Re)Actor:</p>

<ul>
	<li>We allow for three different types of references/pointers:
	
		<ul>
			<li>â€˜owningâ€™ references/pointers, which are conceptually similar to <code>std::unique_ptr&lt;&gt;</code>. In other words, if the â€˜owningâ€™ reference object goes out of scope, the object referenced by it is automatically destroyed. For the time being, we can say that â€˜owningâ€™ references are <em>not</em> reference-counted (and therefore copying them is prohibited, though moving is perfectly fine â€“ just as with <code>std::unique_ptr&lt;&gt;</code>).</li>
			
			<li>â€˜softâ€™ pointers/references. These are quite similar to <code>std::weak_ptr&lt;&gt;</code> (though our â€˜softâ€™ references are created from â€˜owningâ€™ references and not from <code>std:shared_ptr&lt;&gt;</code>), and to Java WeakRef/SoftRef. However, I donâ€™t want to call them â€˜weak referencesâ€™ to avoid confusion with <code>std::weak_ptr&lt;&gt;</code> â€“ which is pretty similar in concept, but works only in conjunction with <code>std::shared_ptr&lt;&gt;</code>, hence the name â€˜soft referencesâ€™.
			
				<ul>
					<li>Most importantly â€“ trying to dereference (in C++, call an <code>operator -&gt;()</code>, <code>operator *()</code>, or <code>operator[]</code>) our â€˜softâ€™ reference when the â€˜owningâ€™ reference is already gone is an invalid operation (leading â€“ depending on the mode of operation â€“ to an exception or to UB; more on different modes of operation below). </li>
				</ul>
			</li>
			
			<li>â€˜nakedâ€™ pointers/references. These are just our usual C/C++ pointers.</li>
		</ul>
	</li>
	
	<li>Our (Re)Actor doesnâ€™t use any non-const globals. Avoiding non-const globals is just good practice â€“ and an especially good one in case of (Re)Actors (which are not supposed to interact beyond exchanging messages).</li>
	
	<li>Now, weâ€™re saying that <em>whatever forms the state of our (Re)Actor (in fact â€“ it is all the members of our SpecificReactor) MUST NOT have any naked pointers or references (though both â€˜owningâ€™ and â€˜softâ€™ references are perfectly fine</em>). This is quite easy to ensure â€“ and is extremely important for us to be able to provide some of the capabilities which weâ€™ll discuss below.</li>
	
	<li>As for collections â€“ we can easily say that theyâ€™re exempt from the rules above (i.e. we donâ€™t care how collections are implemented â€“ as long as theyâ€™re working). In addition, memory allocated by collections may be exempt from other requirements discussed below (weâ€™ll note when it happens, in appropriate places).</li>
</ul>

<p>With this memory allocation model in mind, I am very comfortable to say that </p>

<p class="blockquote"><em>It is sufficient to represent ANY data structure, both theoretically and practically</em></p>

<p>The theoretical part can be demonstrated by establishing a way to represent an arbitrary graph with our allocation model. This can be achieved via two steps:  (a) first, we can replace all the refs in an arbitrary graph by â€˜softâ€™ refs, and (b) second, there is always a set of refs which make all the nodes in the graph reachable exactly once; by replacing exactly this second set of references with our â€˜owningâ€™ refs, we get the original arbitrary graph represented with our â€˜owning refsâ€™+â€˜soft refsâ€™.</p>

<p>As for a practical part â€“ IMO, it is quite telling that Iâ€™ve seen a very practical over-a-million-LOC codebase which worked exactly like this, and it worked like a charm too.</p>

<p>BTW, </p>

<p class="blockquote"><em>most of the findings in this article are also applicable to a more-traditional-for-C++11-folks allocation model of â€˜shared ptrâ€™+â€˜weak ptrâ€™</em></p>

<p>(though for single-threaded access, so atomic requirements donâ€™t apply; also, weâ€™ll still need to avoid â€˜nakedâ€™ pointers within the state of our (Re)Actor). However, it is a bit simpler to tell the story from the point of view of â€˜owningâ€™ refs +â€˜softâ€™ refs, so for the time being weâ€™ll stick to the memory allocation model discussed above.</p>

<h2>An all-important observation</h2>

<p>Now, based on our memory allocation model, weâ€™re able to make an all-important</p>

<p>Observation 1. Whenever our program counter is within the Infrastructure Code but is outside of <code>react()</code>, there are no â€˜naked pointersâ€™ to (Re)Actorâ€™s heap. </p>

<p>This observation directly follows from a prohibition on having â€˜naked pointersâ€™ within (Re)Actorâ€™s state: when weâ€™re outside of <code>react()</code>, there are no â€˜naked pointersâ€™ (pointing to the heap of our (Re)Actor) on the stack; and as there are no non-const globals, and there are â€˜naked pointersâ€™ within the heap itself either â€“ well, weâ€™re fine.</p>
<h2>Modes of operation</h2>

<p>Now, letâ€™s see what how we can implement these â€˜owning refsâ€™ and â€˜soft refsâ€™. Actually, the beauty of our memory model is that it <em>describes</em> WHAT weâ€™re doing, but <em>doesnâ€™t prescribe</em> HOW it should be implemented. This leads us to several possible implementations (or â€˜modes of operationâ€™) for â€˜owning refsâ€™/â€˜soft refsâ€™. Letâ€™s consider some of these modes.</p>

<h3>â€˜Fastâ€™ mode</h3>

<p>In â€˜Fastâ€™ mode, â€˜owning refs/pointersâ€™ are more or less <code>std::unique_ptr&lt;&gt;</code>s â€“ and â€˜soft refs/pointersâ€™ are implemented as simple â€˜naked pointersâ€™. </p>

<p>With this â€˜fastâ€™ mode, we get the best possible speed, but we donâ€™t have any safety or reallocation goodies. Still, it might be perfectly viable for some production deployments where speed is paramount (and crashes are already kinda-ruled out by thorough testing, running new in production in â€˜safeâ€™ mode for a while, etc. etc.).</p>

<h3>â€˜kinda-Safeâ€™ mode</h3>

<p>In a â€˜kinda-Safeâ€™ mode, weâ€™ll be dealing with â€˜dangling pointersâ€™; the idea is to make sure that â€˜dangling pointersâ€™ (if there are any) donâ€™t cause memory corruption but cause an exception instead. </p>

<p>First of all, letâ€™s note though that because of the semantics of â€˜owning pointersâ€™, they cannot be â€˜danglingâ€™, so we need to handle only â€˜softâ€™ and â€˜nakedâ€™ pointers, and references. </p>

<h3>â€˜Danglingâ€™ soft references/pointers</h3>

<p>To deal with â€˜danglingâ€™ soft-pointers/references, we could go the way of double-reference-counting (similar to the one done by <code>std::weak_ref&lt;&gt;</code> â€“ which actually uses the ages-old concept of <em>tombstones</em>), but we can do something better (and BTW, the same technique <em>might</em> be usable to implement <code>std::weak_ref&lt;&gt;</code> too â€“ though admittedly generalizing our technique to multi-threaded environment is going to be non-trivial).</p>

<p>Our idea will be to:</p>

<ul>
	<li>Say that our allocator is a â€˜bucket allocatorâ€™ or â€˜slab allocatorâ€™. Whatâ€™s important is that if there is an object at memory address X, then there cannot be an object crossing memory address X, <em>ever</em>.
	
		<ul>
			<li>Letâ€™s note that memory allocated by collections for their internal purposes is exempt from this requirement (!).</li>
		</ul>
	</li>
	
	<li>Say that each allocated object has an ID â€“ positioned right before the object itself. IDs are just incremented forever-and-ever for each new allocation (NB: 64-bit ID, being incremented 1e9 times per second, will last without wraparound for about 600 years â€“ good enough for most of the apps out there if you ask me).</li>
	
	<li>Each of our â€˜owning refsâ€™ and â€˜soft refsâ€™, in addition to the pointer, contains an ID of the object it is supposed to point to.</li>
	
	<li>Whenever we need to access our â€˜owning refâ€™ or â€˜soft refâ€™ (i.e. weâ€™re calling <code>operator -&gt;()</code> or <code>operator *()</code> to convert from our ref to naked pointer), weâ€™re reading the ID from our ref, AND reading the ID which is positioned right before the object itself â€“ and comparing them. If there is a mismatch, we can easily raise an exception (as the only reason for such a mismatch is that the object has been deleted).
	
		<ul>
			<li>This approach has an inherent advantage over a tombstone-based one: as we do not need an extra indirection â€“ this implementation is inherently more cache friendly. More specifically, weâ€™re not risking an extra read from L3 cache or, Ritchie forbid, from main RAM, and the latter can take as much as 150 CPU cycles easily. On the other hand, for our ID-reading-and-comparing, weâ€™ll be usually speaking only about the cost of 2â€“3 CPU cycles.</li>
		</ul>
	</li>
</ul>

<p>NB: of course, it IS still possible to use double-ref-counting/tombstones to implement â€˜kinda-Safe modeâ€™ â€“ but at this time, I prefer an ID-based implementation as it doesnâ€™t require an extra indirection (and such indirections, potentially costing as much as 150 cycles, can hurt performance pretty badly). OTOH, if it happens that for some of the real-world projects tombstones work better, it is always still possible to implement â€˜kinda-Safe modeâ€™ via a traditional tombstone-based approach.</p>

<h3>â€˜Danglingâ€™ naked references/pointers</h3>

<p>With naked references/pointers â€“ well, strictly speaking, we cannot provide strict guarantees on their safety (thatâ€™s why the mode is â€˜kinda-Safeâ€™, and not â€˜really-Safeâ€™). However, quite a few measures are still possible to both detect such accesses in debugging, <em>and</em> to mitigate the impact if it happens in production:</p>

<ul>
	<li>Most importantly, our allocation model already has a restriction on life time of â€˜nakedâ€™ pointers, which already significantly lowers the risks of â€˜nakedâ€™ pointers dangling around. </li>
	
	<li>In addition, we can ensure that within our (Re)Actor allocator, we do NOT really free memory of deleted objects (leaving them in a kind of â€˜zombieâ€™ state) â€“ that is, until weâ€™re out of the <code>react()</code> function. This will further reduce risks of memory corruption due to a â€˜danglingâ€™ pointer (just because within our memory allocation model, <em>all</em> the dangling naked pointers will point to â€˜zombieâ€™ objects and nothing but â€˜zombieâ€™ objects). As for increased memory usage due to delayed reclaiming of the memory â€“ in the vast majority of use cases, it wonâ€™t be a problem because of a typical <code>react()</code> being pretty short with relatively few temporaries.
	
		<ul>
			<li>In debug mode, we may additionally fill deleted objects with some garbage. In addition, when out of <code>react()</code>, we can detect that the garbage within such deleted objects is still intact; for example, if we filled our deleted objects with 0xDEAD bytes, we can check that after leaving <code>react()</code> deleted objects still have the 0xDEAD pattern â€“ and raise hell if they donâ€™t (messing with the contents of supposedly deleted objects would indicate severe problems within the last call to <code>react()</code>).</li>
			
			<li>In production mode, we can say that our destructors leave our objects in a â€˜kinda-safeâ€™ state; in particular, â€˜kinda-safeâ€™ state may mean that further pointers (if any) are replaced with <code>nullptr</code>s (and BTW, within our memory allocation model, this may be achieved by enforcing that destructors of â€˜owning pointers/refsâ€™ and â€˜soft pointers/refsâ€™ are setting their respective pointers to <code>nullptr</code>s; implementing â€˜kinda-safeâ€™ state of collections is a different story, though, and will require additional efforts).
			
				<ul>
					<li>This can help to contain the damage if a â€˜danglingâ€™ pointer indeed tries to access such a â€˜zombieâ€™ object â€“ at least we wonâ€™t be trying to access any further memory based on garbage within the â€˜zombieâ€™.</li>
				</ul>
			</li>
		</ul>
	</li>
</ul>

<h3>â€˜Safe with relocationâ€™ mode</h3>

<p>In a â€˜Safe with relocationâ€™ mode, in addition to dealing with â€˜danglingâ€™ soft refs, weâ€™ll be allowing to relocate our allocated objects. This will allow us to eliminate dreaded â€˜external fragmentationâ€™ â€“ which tends to cause quite a bit of trouble for long-running systems â€“ with lots of CPU pages having a single object in them being allocated some memory (which in turn, if we cannot possibly relocate those single objects, tends to cause <em>lots</em> of memory waste).</p>

<p>To implement relocation, in addition to the trickery discussed for â€˜Safeâ€™ mode, weâ€™ll be doing the following:</p>

<ul>
	<li>All relocations will happen only outside of the <code>react()</code> function (i.e. when there are no â€˜nakedâ€™ pointers to the heap, phew)
		<ul>
			<li>How exactly to relocate objects to ensure freeing pages is outside the scope of this article; here, we are concentrating only on the question of how to ensure that everything works <em>after</em> weâ€™re done relocating some of our objects</li>
		</ul>
	</li>	
	
	<li>Keep a per-(Re)Actor-heap â€˜relocation mapâ€™ â€“ a separate map of object IDs (the ones used to identify objects, as discussed in â€˜Safeâ€™ mode) into new addresses.
		<ul>
			<li>To keep the size of â€˜relocation mapâ€™ from growing forever-and-ever, we could:
				<ul>
					<li>For each of our heap objects, keep a counter of all the â€˜owningâ€™ and â€˜softâ€™ pointers to the object.</li>
			
					<li>Whenever we relocate object, copy this counter to the â€˜relocation mapâ€™. Here, it will have the semantics of â€˜remaining pointers to be fixedâ€™.</li>
			
					<li>Whenever we update our â€˜owningâ€™ or â€˜softâ€™ pointer as described below, decrement the â€˜remaining pointers to be fixedâ€™ counter (and when it becomes zero, we can safely remove the entry from our â€˜relocation mapâ€™).</li>
				</ul>
			</li>
		
			<li>An alternative (or complementing) approach is to rely on â€˜traversingâ€™, as described below.</li>
			
			<li>Exact implementation details of the â€˜relocation mapâ€™ donâ€™t really matter much; as it is accessed only very infrequently, search times within it are not important (though I am <em>not</em> saying we should use linear search there).</li>
		</ul>
	</li>
	
	<li>Whenever we detect access to a non-matching object ID (i.e. an â€˜owning pointerâ€™ or â€˜soft pointerâ€™ tries to convert to a â€˜nakedâ€™ pointer and finds out that the object ID in heap is different from the ID they have stored), instead of raising an exception right away, weâ€™ll look into the â€˜relocation mapâ€™ using the object ID within the pointer trying to access the object, and then:
		<ul>
			<li>If the object with such an object ID is found in the â€˜relocation mapâ€™, we update our â€˜owning pointerâ€™ or â€˜soft pointerâ€™ to a new value and continue.</li>
			
			<li>If the object with the ID within the pointer is not found, the object has been deleted, so we raise exception to indicate access attempt to a deleted object (just as for â€˜safe modeâ€™ above).</li>
		</ul>
	</li>
	
	<li>If our relocation has led to a page being freed (and decommitted), attempts to dereference â€˜owning pointersâ€™ or â€˜soft pointersâ€™ may cause a CPU access violation. In such cases, we should catch the CPU exception, and once again look into our â€˜relocation mapâ€™ using exactly the same logic as above (and causing either updating the current pointer, or an app-level exception).
		<ul>
			<li>To make sure that our system works as intended (and that all the pointers can still rely on an object ID always being before the object), we need to take the following steps:
				<ul>
					<li>After decommitting the page, we still need to keep address space for it reserved.</li>
					<li>In addition, we need to keep track of such decommitted-but-reserved pages in a some kind of â€˜page mapâ€™, and make sure that if we reuse the same page, we use it <em>only</em> for allocations of exactly the same â€˜bucket sizeâ€™ as before.</li>
					<li>While this might sound restrictive, for practical x64 systems it is usually not a big deal because (as weâ€™re decommitting the page) weâ€™ll be wasting only <em>address </em>space, and not <em>actual memory</em>. As modern x64 OSs tend to provide processes with 47-bit address space, this means that for a program which uses not more than 100G of RAM at any given time, and uses 100 different bucket sizes, in the very worst case, weâ€™ll waste at most 10000G of address space, and this is still well below that 47-bit address space we normally have.</li>
				</ul>
			</li>
		</ul>
	</li>
</ul>

<p>Bingo! Weâ€™ve got (kinda-)safe implementation â€“ and with the ability to compact our heap too, if we wish.</p>

<h2>Traversing SpecificReactor state</h2>

<p>In spite of all our efforts discussed above, in certain cases, there <em>might</em> be situations when the size of our â€˜page mapâ€™ and especially â€˜relocation mapâ€™ will grow too large. While I expect such situations to be <em>extremely</em> rare, it is still nice to know that there is a way to handle them.</p>

<p>If we say that for every object within our class <code>SpecificReactor</code>, there is a <code>traverse()</code> function (with <code>traverse()</code> at each level doing nothing but calling <code>traverse()</code> for each of child objects) then after calling <code>traverse()</code> for the whole <code>SpecificReactor</code>, we can be sure that <em>all</em> the pointers have been dereferenced, and therefore were fixed if applicable; as a result â€“ after such a <code>traverse()</code> â€“ our â€˜relocation mapâ€™ is no longer necessary and can be cleaned (BTW, if weâ€™re doing <code>traverse()</code> frequently enough, we may avoid storing the reference count, which was mentioned above in the context of cleaning up the â€˜relocation mapâ€™).</p>

<p>Moreover, after such a call to <code>SpecificReactor::traverse()</code>, we can be sure that there are no more pointers to decommitted pages, which means that â€˜page mapâ€™ can be cleaned too. </p>

<p>On the one hand, letâ€™s note that for (Re)Actors with a large state, traversing the whole state may take a while (especially if the state is large enough to spill out of the CPU caches) â€“ which may be undesirable for latency-critical apps. On the other hand, in such cases it is <em>usually</em> possible to implement traversing in an incremental manner (relying on the observation that any newly created objects are not a problem) â€“ but all methods I know for such incremental traversals require us to be very careful about object moves (from a not-traversed-yet into a supposedly-already-traversed area) and about invalidating collection iterators. Still, it is <em>usually</em> possible and fairly easy to write such an incremental traversal â€“ albeit an ad hoc one (i.e. taking the specifics of the app into account).</p>

<h2>Further discussion planned</h2>

<p>Actually, this is not the end of discussion about (Re)Actors and their allocators. In particular, I hope to discuss how to use such allocators to implement (Re)Actor serialization (and as mentioned in [<a href="Ignatchenko.xml#id([NoBugs17])">NoBugs17</a>], serialization of the (Re)Actor state is necessary to achieve quite a few (Re)Actor goodies, including such big things as Replay-Based Regression Testing and production post-factum debugging).</p>

<p><img src="http://accu.org/content/images/journals/ol139/Ignatchenko/Ignatchenko-01.png" /></p>

<p>Cartoon by Sergey Gordeev from Gordeev Animation Graphics, Prague</p>


<h2>References</h2>

<p class="bibliomixed"><a id="[Go2010]"></a>[Go2010] â€˜Share Memory By Communicatingâ€™, The Go Blog, <a href="https://blog.golang.org/share-memory-by-communicating">https://blog.golang.org/share-memory-by-communicating</a></p>

<p class="bibliomixed"><a id="[Henney17]"></a>[Henney17] Kevlin Henney, ACCU2017, â€˜Thinking Outside the Synchronisation Quadrantâ€™</p>

<p class="bibliomixed"><a id="[Loganberry04]"></a>[Loganberry04] David â€˜Loganberryâ€™, Frithaes! â€“ an Introduction to Colloquial Lapine!, <a href="http://bitsnbobstones.watershipdown.org/lapine/overview.html">http://bitsnbobstones.watershipdown.org/lapine/overview.html</a></p>

<p class="bibliomixed"><a id="[NoBugs10]"></a>[NoBugs10] â€˜No Bugsâ€™ Hare, â€˜Single Threading: Back to the Future?â€™, <em>Overload</em> #97â€“98, Juneâ€“Aug 2010</p>

<p class="bibliomixed"><a id="[NoBugs15] "></a>[NoBugs15] â€˜No Bugsâ€™ Hare, â€˜Multi-threading at Business-logic Level is Considered Harmfulâ€™, <em>Overload</em> #128, Aug 2015</p>

<p class="bibliomixed"><a id="[NoBugs17]"></a>[NoBugs17] â€˜No Bugsâ€™ Hare, â€˜Deterministic Components for Interactive Distributed Systemsâ€™, ACCU2017, <a href="http://ithare.com/deterministic-components-for-interactive-distributed-systems-with-transcript/">http://ithare.com/deterministic-components-for-interactive-distributed-systems-with-transcript/</a></p>

<p class="bibliomixed"><a id="[Sutter11]"></a>[Sutter11] Herb Sutter, â€˜Garbage Collection Synopsis, and C++â€™. <a href="https://herbsutter.com/2011/10/25/garbage-collection-synopsis-and-c/">https://herbsutter.com/2011/10/25/garbage-collection-synopsis-and-c/</a></p>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
