<table><tr><td style="">aaronpuchert added a comment.
</td><a style="text-decoration: none; padding: 4px 8px; margin: 0 8px 8px; float: right; color: #464C5C; font-weight: bold; border-radius: 3px; background-color: #F7F7F9; background-image: linear-gradient(to bottom,#fff,#f1f0f1); display: inline-block; border: 1px solid rgba(71,87,120,.2);" href="https://phabricator.kde.org/D22182">View Revision</a></tr></table><br /><div><div><blockquote style="border-left: 3px solid #8C98B8;
color: #6B748C;
font-style: italic;
margin: 4px 0 12px 0;
padding: 8px 12px;
background-color: #F8F9FC;">
<div style="font-style: normal;
padding-bottom: 4px;">In <a href="https://phabricator.kde.org/D22182#491716" style="background-color: #e7e7e7;
border-color: #e7e7e7;
border-radius: 3px;
padding: 0 4px;
font-weight: bold;
color: black;text-decoration: none;">D22182#491716</a>, <a href="https://phabricator.kde.org/p/mswan/" style="
border-color: #f1f7ff;
color: #19558d;
background-color: #f1f7ff;
border: 1px solid transparent;
border-radius: 3px;
font-weight: bold;
padding: 0 4px;">@mswan</a> wrote:</div>
<div style="margin: 0;
padding: 0;
border: 0;
color: rgb(107, 116, 140);"><p>I don't suspect our <tt style="background: #ebebeb; font-size: 13px;">..Locker</tt> constructors will ever (i.e. not anytime prior to the heat death of the universe) take a different lock. For that reason I am partial to making the lock implicit wherever possible.</p></div>
</blockquote>
<p>That's a bold statement. ;)</p>
<blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><p>Would we be able to change the constructor to default to <tt style="background: #ebebeb; font-size: 13px;">DUChain::lock()</tt>? I'm not familiar with C++'s rules w.r.t. default function/constructor values. If we can't make it a function call, would we be able to make it a plain-old global variable?</p></blockquote>
<p>In principle we could have that as default argument, although that requires including <tt style="background: #ebebeb; font-size: 13px;">duchain.h</tt> in <tt style="background: #ebebeb; font-size: 13px;">duchainlock.h</tt>.</p>
<p>But I would actually advise against this. I would either make the argument explicit (meaning there is no default), or always implicit (meaning there is no way to specify another lock). If we make it implicit, then we don't have to use <tt style="background: #ebebeb; font-size: 13px;">DUChain::lock()</tt> in the annotation, because that effectively becomes an implementation detail and can use a mock object instead.</p>
<blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><p>Regardless, I would be in favor of the mock lock approach because I like keeping verbosity down when it really doesn't suit an immediate need.</p></blockquote>
<p>I don't find it terribly verbose. Also there are other locks used in the du-chain code, and I think it doesn't hurt to be a little bit more explicit.</p>
<blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><p>That is currently not supported.</p></blockquote>
<p>That sounds like it could be an interesting project. It looks like they are pretty close since they have this whole "TRY_ACQUIRE" annotation which isn't exactly what we're looking for but almost. If it could be instead predicated on, say, the return value of a public boolean method or instance variable post-invocation, that would be enough for our needs. I have not looked into how this works and what kinds of things it can check.</p></blockquote>
<p>It would require adding an additional attribute, which I'm hesitant to do, since we have a good workaround.</p>
<blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><p>I assume it does a very basic check at the caller to see that the return value is <em>immediately</em> used and that the failed-to-lock branch doesn't require the lock. That is mere speculation.</p></blockquote>
<p>It doesn't work like that actually, the branch point can be considerably later. Basically we check if a branch condition correlates to the return value of a try-acquire call and if it does, we assumes the lock is acquired on that branch.</p>
<div class="remarkup-code-block" style="margin: 12px 0;" data-code-lang="text" data-sigil="remarkup-code-block"><pre class="remarkup-code" style="font: 11px/15px "Menlo", "Consolas", "Monaco", monospace; padding: 12px; margin: 0; background: rgba(71, 87, 120, 0.08);">void k() {
bool hold = mu.TryLock();
bool hold2 = hold;
if (b)
hold = true;
if (hold) {
a = 8; // warning: writing variable 'a' requires holding mutex 'mu' exclusively [-Wthread-safety-analysis]
}
if (hold2) {
a = 8; // No warning here.
mu.Unlock();
}
}</pre></div>
<p>There are some deficits in the analysis of try-acquires, but that's mostly because it just isn't used very often.</p>
<blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><p>Not sure that will be needed if one of us adds support for that slight augmentation to clang's existing TRY_ACQUIRE annotation. Currently whenever this timeout try-lock behaviour is performed, the <tt style="background: #ebebeb; font-size: 13px;">locked()</tt> instance method is immediately invoked and usually leads to either returning or continuing with the rest of the function.</p></blockquote>
<p>I find the current behavior confusing actually. A constructor is meant to construct a "valid" object or fail (that's the idea of RAII), yet here it might not be valid and we always have to check with some function. This isn't very idiomatic and also error-prone. Getting rid of this pattern is a value in and of itself, independent of whether we want to pursue the analysis.</p>
<blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><p>Adopting means that we don't lock the mutex, we just take over an already locked mutex. (See e.g. the second constructor of a <a href="https://en.cppreference.com/w/cpp/thread/lock_guard/lock_guard" class="remarkup-link" target="_blank" rel="noreferrer">lock_guard</a>.) The annotation for that is <tt style="background: #ebebeb; font-size: 13px;">REQUIRES(...)</tt> on the constructor.</p>
<p>Then you can do:</p>
<div class="remarkup-code-block" style="margin: 12px 0;" data-code-lang="text" data-sigil="remarkup-code-block"><pre class="remarkup-code" style="font: 11px/15px "Menlo", "Consolas", "Monaco", monospace; padding: 12px; margin: 0; background: rgba(71, 87, 120, 0.08);">if (!DUChain::lock().lockForRead())
return; // or error handling...
DUChain{Read,Write}Locker lock(DUChain::lock(), std::adopt_lock);</pre></div></blockquote>
<p>I am not sure I follow your example. Obviously we would just invoke <tt style="background: #ebebeb; font-size: 13px;">DUChain{Read,Write}Locker</tt> instead of going through this sadistic exercise. It is unclear to me when we would actually want to adopt a lock in a <tt style="background: #ebebeb; font-size: 13px;">DUChain{Read,Write}Locker</tt> constructor invocation.</p></blockquote>
<p>You are aware that the code this is meant to replace isn't just a constructor call? This is the pattern that we find right now:</p>
<div class="remarkup-code-block" style="margin: 12px 0;" data-code-lang="text" data-sigil="remarkup-code-block"><pre class="remarkup-code" style="font: 11px/15px "Menlo", "Consolas", "Monaco", monospace; padding: 12px; margin: 0; background: rgba(71, 87, 120, 0.08);">DUChain{Read,Write}Locker lock(DUChain::lock(), /*timeout*/ 100);
if (!lock.locked())
return; // or error handling...</pre></div>
<p>What do you find sadistic about my proposed replacement?</p>
<blockquote style="border-left: 3px solid #a7b5bf; color: #464c5c; font-style: italic; margin: 4px 0 12px 0; padding: 4px 12px; background-color: #f8f9fc;"><p>That said, adopting a lock seems plenty relevant to the many functions present in KDevelop that expect prior locking, e.g. methods beginning with <tt style="background: #ebebeb; font-size: 13px;">ENSURE_CAN_READ</tt>.</p></blockquote>
<p>Adopting means the lock will be released upon leaving the function. That's not what we want there, we want an annotation on the function <tt style="background: #ebebeb; font-size: 13px;">REQUIRES_SHARED(...)</tt>. Remember that the main purpose of scoped locks is to ensure release on all code paths. If you don't want to release anything, there is no reason for a scoped lock.</p></div></div><br /><div><strong>REPOSITORY</strong><div><div>R32 KDevelop</div></div></div><br /><div><strong>REVISION DETAIL</strong><div><a href="https://phabricator.kde.org/D22182">https://phabricator.kde.org/D22182</a></div></div><br /><div><strong>To: </strong>mswan, KDevelop<br /><strong>Cc: </strong>aaronpuchert, kdevelop-devel, hmitonneau, christiant, glebaccon, domson, antismap, iodelay, alexeymin, geetamc, Pilzschaf, akshaydeo, surgenight, arrowd<br /></div>