[PATCH] Removing the static initializer in ManagedStatic.cpp by using llvm_call_once to initialize the ManagedStatic mutex.

David Majnemer david.majnemer at gmail.com
Mon Oct 27 17:25:07 PDT 2014


On Mon, Oct 27, 2014 at 1:52 PM, Chris Bieneman <beanz at apple.com> wrote:

> A few notes.
>
> According to Chandler's related RFC (
> http://lists.cs.uiuc.edu/pipermail/llvmdev/2014-September/077081.html) we
> do not have a requirement that all platforms support <atomic>.
>

I quickly chatted with Chandler and he mentioned that <atomic> is fine so
long as we are sticking to std::atomic<integer-type>.
He wanted to convey that we want to use <atomic> more, not that we can't
use it at all today.


>
> >>! In D5922#11, @majnemer wrote:
> > I'm afraid not.  Because your implementation uses operations which are
> not atomic, it has undefined behavior in the eyes of C++11.
> >
> > C++11 [intro.multithread]p21:
> > The execution of a program contains a data race if it contains two
> conflicting actions in different threads, at least one of which is not
> atomic, and neither happens before the other. Any such data race results in
> undefined behavior.
>
> I think you're misconstruing what this means. Your statement would seem to
> imply that until C++11 nobody ever managed to write thread safe code--
> which is absurd.


I'm not implying that but relying on undefined behavior isn't great either.

At best, I dislike declaring the variable "volatile".  I would instead
prefer casting to volatile and loading, this would make things more
explicit.


> There are certainly undefined behaviors in the implementation I've
> provided, but they are known, and appropriately handled.
>

I don't think you can "handle undefined behavior" by definition.


>
> Both the MemFences, and the tmp in the else branch of the original patch
> can be safely removed, and the code can be re-written into a while loop for
> performance. An example implementation would be:
>
> ```
> enum InitStatus {
>   Done = -1,
>   Uninitialized = 0,
>   Wait = 1
> };
>
> void llvm::call_once(once_flag& flag, void (*UserFn)(void)) {
>   while (flag != Done) {
>     if (flag == Wait) {
>

I would much rather prefer you do a single load of flag instead.  Also, you
don't have a fence on the Wait => Done side; I believe this can lead to
unfortunate reorderings.


>       ::Sleep(1);
>       continue;
>     }
>
>     sys::cas_flag old_val = sys::CompareAndSwap(&flag, Wait,
> Uninitialized);
>     if (old_val == Uninitialized) {
>       UserFn();
>       sys::MemoryFence();
>       flag = Done;
>     }
>   }
> }
> ```
>
> I (and some of my colleagues) believe that this is perfectly safe code,
> and it eliminates unnecessary MemFences (which you raised concerns about).
> Do you see a problem with this implementation (other than the fact that it
> doesn't use std::atomic)?
>
> I don't have any particular opposition to using std::atomic, however since
> we don't have an enforced requirement I'm not sure we can assume we have
> it. Please keep in mind I think we still support deploying back to Windows
> XP.
>
> http://reviews.llvm.org/D5922
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20141027/6dfb8530/attachment.html>


More information about the llvm-commits mailing list