<h1 id="p3497r0-guarded-objects">p3497R0 - guarded objects</h1>
<p>guarded objects - make the relationship between objects and their locking mechanism explicitly expressable and hard to use incorrect</p>
<p>11 November 2024</p>
<h3 id="authors-">Authors:</h3>
<ul>
<li>Jan Wilmans (janwilmans at gmail.com)</li>
</ul>
<h3 id="audience-">Audience:</h3>
<ul>
<li>Library Evolution Working Group (Design &amp; Target)</li>
<li>Library Working Group (Wording and Consistency)</li>
</ul>
<h3 id="project-">Project:</h3>
<ul>
<li>ISO JTC1/SC22/WG21: Programming Language C++</li>
</ul>
<h3 id="current-version-">Current version:</h3>
<ul>
<li><a href="https://github.com/janwilmans/proposals/blob/master/pXXXXR0_guarded_objects.md">https://github.com/janwilmans/proposals/blob/master/pXXXXR0_guarded_objects.md</a></li>
</ul>
<h3 id="reply-to-">Reply To:</h3>
<ul>
<li>Jan Wilmans <a href="&#109;&#x61;&#x69;&#x6c;&#116;&#111;&#58;&#x6a;&#x61;&#x6e;&#x77;&#105;&#x6c;&#x6d;&#x61;&#x6e;&#115;&#64;&#x67;&#109;&#97;&#105;&#x6c;&#46;&#99;&#x6f;&#109;">&#x6a;&#x61;&#x6e;&#x77;&#105;&#x6c;&#x6d;&#x61;&#x6e;&#115;&#64;&#x67;&#109;&#97;&#105;&#x6c;&#46;&#99;&#x6f;&#109;</a></li>
</ul>
<h1 id="abstract">Abstract</h1>
<p>In multithreaded programming locking mechanisms are used to prevent concurrent access to data. Common practice is to create a locking mechansim, lets say an <strong>std::mutex</strong> along side the <strong>data</strong> it is protecting.
However, the relationship between the mutex and the data is only implied and expressed in code only by naming variables and/or &#39;doing it right&#39; in all places. This proposal improves this by providing a way to clearly express the relationship and make it impossible to access the data without locking its associated guarding mechanism.</p>
<h1 id="revision-history">Revision History</h1>
<h2 id="revision-0">Revision 0</h2>
<p>Initial version</p>
<h2 id="motivation">Motivation</h2>
<h2 id="encapsulation-of-locking-logic">Encapsulation of locking logic</h2>
<p>The <code>guarded</code> type encapsulates the locking mechanism and its data, ensuring that lock is acquired and released properly when accessing the protected data. 
By requiring access to the data through the guarded type, you make it harder (or impossible) to accidentally access the data without properly locking it first.</p>
<h2 id="simplified-api">Simplified API</h2>
<p>By combining the the data and its locking mechanism in one type they have the same lifetime and the API for interacting with the protected data becomes clearer. Users don’t have to worry about manually locking and unlocking. Instead, the locking and unlocking can be handled internally within the type. This simplifies the API and reduces the cognitive load.</p>
<h2 id="strong-ownership-semantics-raii-guarantees">Strong ownership semantics / RAII guarantees</h2>
<p>The only wat to access data is to call one of the locking functions, these functions either return an object (a unique_ptr-like object) that owns the lock, or allows you to pass in an operation that is exectured while holding the lock. It can be used in a familiar way, just like you would use a smart pointer, or when passing in the operation, not dealing with the lock directly at all.</p>
<p>The locking mechanism is based on RAII (Resource Acquisition Is Initialization), the type handles acquiring and releasing the lock in a scoped manner. This makes sure the lock is always released no matter how you leave the scope, returning of by an exception for example.</p>
<h2 id="separation-of-concerns">Separation of concerns</h2>
<p>The guarded type eliminates the need to embed thread-safety directly into the design of classes. Such implementations are pessimizing single-threaded use and have locking overhead on every API call. 
An example of a &#39;synchronized&#39; queue:</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;queue&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;mutex&gt;</span></span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;cs_plain_guarded.h&gt;</span></span>

template <span class="hljs-params">&lt;typename T&gt;</span>
class <span class="hljs-class">SynchronizedQueue
</span>{
<span class="hljs-symbol">public:</span>
    bool Empty() const
    {
<span class="hljs-symbol">        std:</span>:unique_lock<span class="hljs-params">&lt;std::mutex&gt;</span> lock(m_mtx);
        return m_q.empty();
    }

    void Push(T t)
    {
<span class="hljs-symbol">        std:</span>:unique_lock<span class="hljs-params">&lt;std::mutex&gt;</span> lock(m_mtx);
        m_q.push(<span class="hljs-symbol">std::</span>move(t));
    }

    T Pop()
    {
<span class="hljs-symbol">        std:</span>:unique_lock<span class="hljs-params">&lt;std::mutex&gt;</span> lock(m_mtx);
        T t(m_q.front());
        m_q.pop();
        return t;
    }

    mutable <span class="hljs-symbol">std::</span>mutex m_mtx;  

    <span class="hljs-comment">// here there is the implied agreement, that 'm_mtx' should be locked </span>
    <span class="hljs-comment">// before accessing m_q, however nothing enforces this.</span>
<span class="hljs-symbol">    std:</span>:queue<span class="hljs-params">&lt;T&gt;</span> m_q;           
};
</code></pre><p>Notice that the code above is fragile in the sense that every public method <strong>must</strong> acquire the lock and forgetting to do so is only checkable by reviewing.</p>
<p>Alternative with a guarded lock from the <a href="https://github.com/copperspice/cs_libguarded"><code>cs_libguarded</code></a> library:</p>
<pre><code><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">using</span> GuardedQueue = libguarded::plain_guarded&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">queue</span>&lt;T&gt;&gt;;

<span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">example</span><span class="hljs-params">()</span>
</span>{
    GuardedQueue&lt;<span class="hljs-keyword">int</span>&gt; guarded_queue;

    <span class="hljs-comment">// it is not possible to access the std::queue directly, without calling lock()</span>
    <span class="hljs-keyword">return</span> guarded_queue.lock()-&gt;empty(); 
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">example2</span><span class="hljs-params">()</span>
</span>{
    GuardedQueue&lt;<span class="hljs-keyword">int</span>&gt; guarded_queue;

    <span class="hljs-comment">// it is not possible to access the std::queue directly, without calling lock()</span>
    <span class="hljs-keyword">auto</span> locked_q = guarded_queue.lock();

    <span class="hljs-comment">// execute code with the lock held.</span>
    <span class="hljs-keyword">if</span> (locked_q-&gt;empty())
    {
        <span class="hljs-comment">// do things</span>
    }

    <span class="hljs-comment">// lock leaves scope.</span>
}
</code></pre><p><a href="https://godbolt.org/z/ccqaPsfsh">compiler explorer link</a></p>
<p>For demonstration purposes, here is a naive implementation of a &#39;guarding&#39; class that allows you to pass in a function:</p>
<pre><code><span class="hljs-comment">// Naive example how an operation could be passed in, to perform a set of operations while holding the lock</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;mutex&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>

<span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">class</span> naive_guarded {
<span class="hljs-keyword">private</span>:
    T data;
    <span class="hljs-keyword">mutable</span> <span class="hljs-built_in">std</span>::mutex mtx;

<span class="hljs-keyword">public</span>:
    <span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> Func&gt;
    <span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">with_lock</span><span class="hljs-params">(Func&amp;&amp; func)</span> </span>{
        <span class="hljs-built_in">std</span>::lock_guard&lt;<span class="hljs-built_in">std</span>::mutex&gt; lock(mtx);
        <span class="hljs-keyword">return</span> func(data);  <span class="hljs-comment">// Pass the data to the provided function.</span>
    }
};

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    naive_guarded&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&gt; naive_guarded_string;

    <span class="hljs-comment">// Modify the string safely.</span>
    naive_guarded_string.with_lock([](<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&amp; str) {     <span class="hljs-comment">// locking overhead</span>
        str = <span class="hljs-string">"Hello, World!"</span>;
    });

    <span class="hljs-comment">// Read the string safely.</span>
    naive_guarded_string.with_lock([](<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&amp; str) { <span class="hljs-comment">// locking overhead</span>
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; str &lt;&lt; <span class="hljs-string">'\n'</span>;
    });

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre><p>The example above is a demostration only </p>
<p><a href="https://cppcoach.godbolt.org/z/4Evees8nd">compiler explorer link</a></p>
<h2 id="easier-to-reason-about-the-code-better-readability">Easier to reason about the code / better readability</h2>
<p>When locking is tightly coupled with the data it protects, it becomes easier to reason about the behavior of the code. A guarded<std::string> makes it clear that access to the std::string is controlled and synchronized. 
There is also no question about what the lock is protecting, this makes the code more understandble and maintainable. The guarded type abstracts away the details of how the concurrency mechanisms are implemented. Users of the type do not need to worry about whether the data is protected by a mutex, spinlock, or some other concurrency primitive; they get a guarantee the data access is synchronized properly.</p>
<h1 id="design-considerations">Design considerations</h1>
<h2 id="overview">Overview</h2>
<p>The goal of this proposal is to provide a type:</p>
<p>1) better readability and maintainability: by explicitly expressing the relationship between data and its guarding mechanism
2) better thread safely / hard to use incorrectly: by making it impossible to access the data without locking its guarding mechanism
3) make unlocking automatic and exception safe: by returning a RAII object that has ownership of the locked state</p>
<h2 id="synopsis">Synopsis</h2>
<p>An example of what the result could look like:</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"https://raw.githubusercontent.com/copperspice/cs_libguarded/master/src/cs_plain_guarded.h"</span></span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    libguarded::plain_guarded&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&gt; guarded_string;

    <span class="hljs-keyword">auto</span> accessor = guarded_string.lock();  <span class="hljs-comment">// as long as 'accessor' remains in scope, the mechanism remains locked.</span>

    <span class="hljs-comment">// Modify the string safely.         </span>
    *accessor = <span class="hljs-string">"Hello, World!"</span>;  

    <span class="hljs-comment">// Read the string safely.</span>
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; *accessor &lt;&lt; <span class="hljs-string">'\n'</span>;

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}  <span class="hljs-comment">// accessor leaves scope, automatically unlocking</span>
</code></pre><p><a href="https://cppcoach.godbolt.org/z/PTv1MG4oq">compiler explorer link</a></p>
<p>Demonstration of clear separation of the class implementation and the sychronisation of the class.</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;vector&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"https://raw.githubusercontent.com/copperspice/cs_libguarded/master/src/cs_plain_guarded.h"</span></span>

<span class="hljs-keyword">struct</span> person
{
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> name;
};

<span class="hljs-keyword">using</span> <span class="hljs-keyword">contacts_t</span> = <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;person&gt;;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">print</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">contacts_t</span>&amp; contacts)</span>
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> <span class="hljs-keyword">auto</span>&amp; person: contacts)
    {
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; person.name &lt;&lt; <span class="hljs-string">'\n'</span>;
    }
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> 
</span>{
    libguarded::plain_guarded&lt;<span class="hljs-keyword">contacts_t</span>&gt; synchronized_contacts;  <span class="hljs-comment">// specifiy only synchronized access to contacts_t is possible</span>
    print(*synchronized_contacts.lock());                         <span class="hljs-comment">// use of the lock</span>
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre><p>Discuss customizable [thread.req.lockable] interface with Cpp17BasicLockable requirements</p>
<pre><code><span class="hljs-symbol">std:</span><span class="hljs-symbol">:guared&lt;T&gt;</span>;
<span class="hljs-symbol">std:</span><span class="hljs-symbol">:guared&lt;T</span>, L&gt;;   <span class="hljs-regexp">//</span> The type of second argument satisfies the [thread.req.lockable] interface
</code></pre><h1 id="bikeshedding">Bikeshedding</h1>
<p>This section is for naming, conventions and pinning down details to make it suitable for the standard.</p>
<p>As a suggestion: <code>std::guarded&lt;T&gt;</code> would express the intent.</p>
<h1 id="acknowledgements">Acknowledgements</h1>
<ul>
<li>this proposal is based on the the work of Ansel Sermersheim and his <a href="https://github.com/copperspice/cs_libguarded">https://github.com/copperspice/cs_libguarded</a> library</li>
</ul>
<h1 id="references">References</h1>
<ul>
<li><a href="https://github.com/copperspice/cs_libguarded">https://github.com/copperspice/cs_libguarded</a></li>
<li><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4950.pdf">[thread.req.lockable]</a> Cpp17BasicLockable requirements </li>
</ul>
