<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div class="moz-cite-prefix">On 11/4/2021 6:55 PM, Aaron Puchert
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:db5e0086-f352-0c9c-5e16-d32a4d244585@alice-dsl.net">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<div class="moz-cite-prefix">Am 04.11.21 um 04:40 schrieb Randell
Jesup:<br>
</div>
<blockquote type="cite"
cite="mid:1d5a8238-fc4f-91cc-d121-e9042f9ff5c3@mozilla.com">
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8">
<p><br>
</p>
Well, the example I gave doesn't match that, I believe. This is
a value only written on Thread 1, and read on Thread 1 and on
other threads. The requirements for locking would be the lock
around all writes (on thread 1), and lock around all reads from
threads *other* than thread 1. Reads from Thread 1 don't need
to lock, since they're on the only thread that ever writes to
the value. This is conceptually GUARDED_BY(mMutex ||
OnThread1).<br>
<p>The case you're referencing sounds more like "value is freely
written and read on thread 1, then at some point later access
to it is provided to other threads, and after that happens
access is controlled by a mutex" - where the protection regime
changes over time (and may also correlate with specific
methods, like an Init() method).<br>
</p>
</blockquote>
<p>You're absolutely right, I was misreading your case.</p>
<p>What you're describing sounds in fact like thread 1 always has
a shared lock, which it sometimes promotes to exclusive lock,
then demotes to a shared lock again. The other threads can only
acquire shared locks, because thread 1 doesn't ever release its
shared lock. Now there are multiple ways to write that down:</p>
</blockquote>
<p><br>
</p>
<p>You're correct, that's a reasonable way to model this pattern
using locks for everything. The pattern I mentioned is data-race
safe (it shouldn't trigger tsan alerts), but doesn't allow for
validation of safety. It depends on the developers following the
rules for that variable.<br>
</p>
<p><br>
</p>
<blockquote type="cite"
cite="mid:db5e0086-f352-0c9c-5e16-d32a4d244585@alice-dsl.net">
<blockquote type="cite"
cite="mid:1d5a8238-fc4f-91cc-d121-e9042f9ff5c3@mozilla.com">
<p> </p>
<p>We have another pattern that's interesting, where a method
may use Maybe<MutexAutoLock> lock(std::in_place,
mMutex); (Maybe<> is effectively std::optional). This
allows us to lock.reset() instead of using MutexAutoUnlock
lock(mLock), which will lead to an extra lock/unlock pair
compared to Maybe<> and lock.reset().</p>
</blockquote>
<p>Supporting that would be hard, because we'd need to look into
the Maybe type, and then the analysis isn't local anymore.
However, the analysis supports “premature unlocking” of scopes,
i.e. you can write lock.Unlock(). I've even added support for
“relockable scopes”, where you can lock.Lock() again. (Have a
look at the MutexLocker in <a class="moz-txt-link-freetext"
href="https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h"
moz-do-not-send="true">https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h</a>
to see what is possible.)</p>
</blockquote>
<p><br>
</p>
<p>I've checked, and the reason for this pattern is basically as I
mentioned; to allow for unlocking of an RAII lock without an added
lock/unlock pair on exit of the scope. If we can do
"MutexAutoLock lock(mMutex); ...; if (foo) { lock.Unlock();
MethodThatWeCantHoldLockIn(); return; } ..." then we don't need
the Maybe<> stuff.<br>
</p>
<p>I presume support for an RAII unlocker isn't likely soon.<br>
</p>
<p>Thanks again, this has been very helpful.</p>
<p>Randell Jesup, Mozilla<br>
</p>
</body>
</html>