Page MenuHomePhabricator

Change CALL_ONCE_INITIALIZATION implementation to use static initialization
Needs ReviewPublic

Authored by mehdi_amini on Apr 19 2016, 9:51 AM.

Details

Reviewers
kcc
beanz
rnk
Summary

The previous implementation was breaking TSAN.

Diff Detail

Event Timeline

mehdi_amini retitled this revision from to Change CALL_ONCE_INITIALIZATION implementation to use static initialization.
mehdi_amini updated this object.
mehdi_amini added a reviewer: kcc.
mehdi_amini added a subscriber: llvm-commits.
kcc added inline comments.Apr 19 2016, 4:50 PM
include/llvm/PassSupport.h
35

Mmm. I am probably missing something trivial (c++11-ish), but how is this going to work?
For me it looks like you define a static bool function, but never call it.

kcc added inline comments.Apr 19 2016, 4:53 PM
include/llvm/PassSupport.h
35

Aaaah, init_once is a bool variable, not a function. Oh, how much this is confusing...
Will this work with the versions of Visual C++ we use for LLVM?
I remember that static initialization was broken there (until recently?)

Why not std::call_once?

mehdi_amini added inline comments.Apr 19 2016, 5:21 PM
include/llvm/PassSupport.h
35

Yeah it would deserve a comment probably :)
I don't know if static initialization is safe in MSVC 2013 (I know it was announced to be "fixed", but don't know which version).

std::call_once require <mutex>, and I was worried of portability (I had trouble last time I tried to include it), and because of that we have an abstraction of mutex in libSupport I believe).

kcc added a reviewer: rnk.Apr 19 2016, 6:09 PM

Reid, are function scope statics thread-safe with VC++ 2013?

include/llvm/PassSupport.h
35

If you can make it

static bool initialized_once = ...

it will be much more clear and not require a comment.

mehdi_amini edited edge metadata.

Improve readability

rnk added a reviewer: beanz.Apr 20 2016, 10:48 AM
rnk edited edge metadata.

Initialization of local statics is not thread safe in VS 2013, so no, this approach doesn't work. There was some other mingw issue with std::call_once. Ask Chris about this, it's horrible.

These problems will go away when our support floor is VS 2015.

I don't think we have a plan to get rid of MSVC2013 on the short term. We may use a mutex to protect the initialization on MSVC2013 only?

kcc added a comment.Apr 20 2016, 11:14 AM

We may also leave the current code under ifdef VC++ and use your new code for everything else.
If others agree, this will work for me too.

rnk added a comment.Apr 20 2016, 11:24 AM

If we're going to suffer ifdefs, we should just use InitOnceExecuteOnce on the Windows side. I trust it more than this code.

We try not to let LLVM headers transitively pollute the world with windows.h macros, so we would need to have our own definitions of union _RTL_RUN_ONCE and the InitOnceExecuteOnce prototype.

I'm hesitating: the InitiOnceExecuteOnce seems "complex" to setup, knowing that it is *only* for MSVC2013, the tradeoff of leaving the existing code dying with the 2013 support is tempting.

rnk added a comment.Apr 20 2016, 11:52 AM

I already don't trust the existing code, and tsan apparently says it is broken, so if you agree we don't want to use it on Unix, then I *really* don't want to use it on Windows either. :) Feel free to add the ifdef and make it my problem, though.

kcc added a comment.Apr 20 2016, 11:54 AM
In D19271#406788, @joker.eph wrote:

I'm hesitating: the InitiOnceExecuteOnce seems "complex" to setup, knowing that it is *only* for MSVC2013, the tradeoff of leaving the existing code dying with the 2013 support is tempting.

Yea, I agree that we don't want very intrusive changes here.
Reid, what's wrong with leaving the existing code for VC++?

Yeah if we have multi-threaded client on Windows, you're right it should be fixed.
I'll give a try!

Chris B. pointed me to: http://reviews.llvm.org/D5922 which introduced llvm::call_once

It was reverted because of a bug in std::call_once for "aarch64 debian". Anyone knows if we are still bound by this "bug" two years after? The revert didn't give much information, but I guess I can recommit the original revision and see if it breaks again.

rnk added a comment.Apr 25 2016, 10:53 AM
In D19271#410844, @joker.eph wrote:

Chris B. pointed me to: http://reviews.llvm.org/D5922 which introduced llvm::call_once

It was reverted because of a bug in std::call_once for "aarch64 debian". Anyone knows if we are still bound by this "bug" two years after? The revert didn't give much information, but I guess I can recommit the original revision and see if it breaks again.

Technically it's been 17 months (11/2014 to 4/2016). Given that it affected PPC and AArch64, I doubt the problem has been fixed. If you're willing to argue with people using busted platforms, then feel free to use std::call_once. If you just want this issue to go away and never bother you again, I'd stick with the thread safe static local. :)

I'm tempted to commit it just to get a correct bug report...

kcc added a comment.Apr 25 2016, 1:00 PM
In D19271#410997, @joker.eph wrote:

I'm tempted to commit it just to get a correct bug report...

IMHO this is a good idea.
If one of the toolchains we depend on does not support proper C++ we need to have it documented (and then, fixed).

beanz edited edge metadata.Apr 25 2016, 2:15 PM
beanz added a subscriber: beanz.

I’m completely in favor of re-landing the patch. I spent a lot of time working on it with Reid and David Majnemer. Having a call_once mechanism is a good way to avoid certain types of static initialization, so it would be great to have this support in LLVM.

At the time I didn’t have the ability to reproduce the issue that was reported (still probably don’t), and I was pulled off onto another project shortly after.

-Chris

This commit broke NetBSD as noted in http://reviews.llvm.org/D5922

Can you investigate? So that we can have a correct implementation to propose instead.

Thanks.

OK, for now please revert to unbreak the bot.

It looks odd.

gdb --args ../../bin/llvm-tblgen -gen-attrs -I /public/llvm/lib/IR -I /public/llvm/lib/Target -I /public/llvm/include /public/llvm/lib/IR/AttributesCompatFunc.td -o /tmp/lib/IR/AttributesCompatFunc.inc.tmp
(gdb) b main
Breakpoint 1 at 0x4e0758: file /public/llvm/utils/TableGen/TableGen.cpp, line 182.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/bin/llvm-tblgen -gen-attrs -I /public/llvm/lib/IR -I /public/llvm/lib/Target -I /public/llvm/include /public/llvm/lib/IR/AttributesCompatFunc.td -o /tmp/lib/IR/AttributesCompatFunc.inc.tmp

Program received signal SIGSEGV, Segmentation fault.
[Switching to LWP 1]
0x0000000000000000 in ?? ()
(gdb)

I'm investigating.

OK, std::call_once() itself is broken on NetBSD HEAD (I upgraded the buildbot to the snapshot from yesterday to focus on LLDB porting efforts for the upcoming release NetBSD-8).. I will move it to the other NetBSD developers.

CC: @joerg

It is also broken on 7.0, the latest stable release.

Reported in the NetBSD gnats as lib/51139. Once will be squashed I will let you know and stop holding on with it!

Thanks in advance!

Thanks for the quick investigation!

Mehdi, your patch used generic code for the non-'std::call_once' path. How about re-committing, but rather than putting it behind an Windows ifdef, instead just put it behind an "else". Then you can change the guard for using the std variant to just exclude NetBSD until the fix lands. That lets all other platforms make forward progress (including allowing Reid to work on a more reliable Windows-specific implementation if desired).

(I have the same motivation as you I suspect -- my TSan users are complaining. =])

Kamil: what's up on the NetBSD side by the way? If there is no plan to fix the combination of NetBSD + stdlibc++, we'll have to drop support for it at some point.

Kamil: what's up on the NetBSD side by the way? If there is no plan to fix the combination of NetBSD + stdlibc++, we'll have to drop support for it at some point.

It's a bug that affects only libstdc++ in dynamic linking, static works. Also std::call_once libc++ works.

Dropping a platform for little reason isn't reasonable. Maybe we can prepare a CMake test detecting if it works, if so than enable it. After that libstc++ on NetBSD won't be holding back others.

Kamil: what's up on the NetBSD side by the way? If there is no plan to fix the combination of NetBSD + stdlibc++, we'll have to drop support for it at some point.

It's a bug that affects only libstdc++ in dynamic linking, static works. Also std::call_once libc++ works.

Dropping a platform for little reason isn't reasonable. Maybe we can prepare a CMake test detecting if it works, if so than enable it. After that libstc++ on NetBSD won't be holding back others.

"Little reason" is very subjective, so I'll be fine disagreeing with you: if this is an important configuration to support (dynamic link + NetBSD + libstdc++), then I expect someone that maintain this platform to make sure that there is a path forward (i.e. the problem is reported upstream to gcc developer, etc.). If a platform does not support correctly the C++ standard, dropping support for this platform is something that is reasonable to me, and I believe this is how we evaluated every platform/configuration drop in the past (we dropped MSVC2012 for this reason).

(I use platform as the combination of the OS+stdlib+host compiler+...)

Please set some deadline. Debugging it isn't that simple. Probably there is some symbol conflict.

CC: @joerg

There is no deadline in this case, because we'll work around it with macro as long as it is not too bad. I see it more a matter of principle: making sure someone is following up so that it gets resolved "at some point". I don't really like issues that are not tracked : they are less likely to ever be resolved...

I'm declaring myself responsible for this bug. gdb(1) so far isn't helping at all, it disregards setting breakpoints even in main() of:

$ cat test.cpp                                                                                                                                      
#include <iostream>
#include <thread>
#include <mutex>

std::once_flag flag;

int main()
{
    std::call_once(flag, [](){ std::cout << "Simple example: called once\n"; });
}

I will keep you posted.