FYI on std::call_once

339 views
Skip to first unread message

Scott Graham

unread,
Jan 4, 2016, 6:35:09 PM1/4/16
to c...@chromium.org
In https://code.google.com/p/chromium/issues/detail?id=573340#c15 we discovered that std::call_once does not work on Windows XP SP2 which Chrome still supports for a bit longer.

(I'm not clear on whether that's rogue Skia non-cpp-chromium.appspot.com approved usage or not, but just FYI to inform further library approvals/investigations.)

Scott Graham

unread,
Jan 4, 2016, 6:36:08 PM1/4/16
to c...@chromium.org
Actually, I am clear now, that seems to be explicitly banned. But maybe still interesting here.

JF Bastien

unread,
Jan 4, 2016, 6:59:21 PM1/4/16
to Scott Graham, cxx
How is it broken? It fails to enforce thread safety?

Is that also true of C++11's thread-safe guarantee on static function variable initialization? Are they also broken on XP SP2?

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CANHK6RYpBB%3D-LY2BB%2Begp9f9D%2BRrjJUXrUOKRrrjURX-PtAh5g%40mail.gmail.com.

Scott Graham

unread,
Jan 4, 2016, 7:02:52 PM1/4/16
to JF Bastien, cxx
Oh, sorry. That was upthread in the bug but I should have mentioned it here. The binary will fail to launch because it tries to use an OS function that does not exist.

Peter Kasting

unread,
Jan 4, 2016, 7:02:52 PM1/4/16
to JF Bastien, Scott Graham, cxx
On Mon, Jan 4, 2016 at 3:59 PM, JF Bastien <j...@chromium.org> wrote:
How is it broken? It fails to enforce thread safety?

If you read the bug report, it sounds like the issue is that the relevant kernel32 function is not in the DLL on XP SP2, so Chrome can't even load (unresolvable symbol).

PK

JF Bastien

unread,
Jan 4, 2016, 7:05:19 PM1/4/16
to Peter Kasting, Scott Graham, cxx
OK thanks, I read from the comment down :-)

Static function-local init-once works though? Does it work properly, or does the compiler fail to emit the proper locking? That would lead to silent breakage of the same type C++11 tried to fix.

Jeffrey Yasskin

unread,
Jan 4, 2016, 7:06:22 PM1/4/16
to JF Bastien, Scott Graham, cxx
On Mon, Jan 4, 2016 at 3:59 PM, JF Bastien <j...@chromium.org> wrote:
> Is that also true of C++11's thread-safe guarantee on static function
> variable initialization? Are they also broken on XP SP2?

That's listed under "Magic Statics" in
https://msdn.microsoft.com/en-us/library/hh567368.aspx: they say it's
fixed in MSVC 2015, but not before.

JF Bastien

unread,
Jan 4, 2016, 7:10:17 PM1/4/16
to Jeffrey Yasskin, Scott Graham, cxx
It's disallowed by the style guide:

So I guess that's not a problem (yet).

Joe Mason

unread,
Jan 5, 2016, 11:33:18 AM1/5/16
to JF Bastien, Jeffrey Yasskin, Scott Graham, cxx
Nit: that page says right at the top, "Unlike the style guide, the content of this page is advisory, not required." So there's a bit lower bar for usage of "magic statics" to have snuck in.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.

JF Bastien

unread,
Jan 5, 2016, 12:35:35 PM1/5/16
to Joe Mason, Jeffrey Yasskin, Scott Graham, cxx
On Tue, Jan 5, 2016 at 8:33 AM, Joe Mason <joenot...@google.com> wrote:
Nit: that page says right at the top, "Unlike the style guide, the content of this page is advisory, not required." So there's a bit lower bar for usage of "magic statics" to have snuck in.

That's troubling if it's used with the expectation of thread safety but is broken in MSVC versions used.

Can someone with MSVC access check the generated assembly? It should look something like the bold parts below:

$ cat ~/magic-static.cc 
extern int magic();
int user() {
  static int m = magic();
  return m;
}
$ /s/llvm/out/bin/clang++ -std=c++11 -O2 -S ~/magic-static.cc -o - | c++filt 
.text
.file "/usr/local/google/home/jfb/magic-static.cc"
.globl user()
.align 16, 0x90
.type user(),@function
user():                               # @user()
.Lfunc_begin0:
.cfi_startproc
.cfi_personality 3, __gxx_personality_v0
.cfi_lsda 3, .Lexception0
# BB#0:                                 # %entry
pushq %rbx
.Ltmp3:
.cfi_def_cfa_offset 16
.Ltmp4:
.cfi_offset %rbx, -16
movb guard variable for user()::m(%rip), %al
testb %al, %al
jne .LBB0_4
# BB#1:                                 # %init.check
movl guard variable for user()::m, %edi
callq __cxa_guard_acquire
testl %eax, %eax
je .LBB0_4
# BB#2:                                 # %init
.Ltmp0:
callq magic()
.Ltmp1:
# BB#3:                                 # %invoke.cont
movl %eax, user()::m(%rip)
movl guard variable for user()::m, %edi
callq __cxa_guard_release
.LBB0_4:                                # %init.end
movl user()::m(%rip), %eax
popq %rbx
retq

Jeffrey Yasskin

unread,
Jan 5, 2016, 12:57:22 PM1/5/16
to Joe Mason, JF Bastien, Jeffrey Yasskin, Scott Graham, cxx
We also pass -fno-threadsafe-statics to gcc and clang, to avoid paying
for something we can't rely on everywhere:
https://code.google.com/p/chromium/codesearch/#chromium/src/build/common.gypi&l=3722.

On Tue, Jan 5, 2016 at 8:33 AM, Joe Mason <joenot...@google.com> wrote:

Bruce

unread,
Jan 5, 2016, 2:09:19 PM1/5/16
to cxx, joenot...@google.com, j...@chromium.org, jyas...@chromium.org, sco...@chromium.org
It looks to me like VS 2013 does not initialize statics in a thread safe way, whereas VS 2015 does. Disclaimer: I don't know what __Init_thread_header and __Init_thread_footer do and they seem to be important. Searching on those function names finds a bug which indicates that magic statics will not work on XP - silent incorrect behavior. With VS 2015 we may want to compile with /Zc:threadSafeInit- until we drop XP support and are ready to start depending on magic statics. Here's the bug link:

I don't know why the VS 2015 generated code seems to be so much more complex than the code which JF Bastien included.

On VS 2013 the generated code (x86, Release) looks like this:

  00018 a1 00 00 00 00 mov eax, DWORD PTR ?$S1@?1??user@@YAHXZ@4IA
  0001d a8 01 test al, 1
  0001f 75 27 jne SHORT $LN5@user
  00021 83 c8 01 or eax, 1
  00024 a3 00 00 00 00 mov DWORD PTR ?$S1@?1??user@@YAHXZ@4IA, eax
  00029 c7 45 fc 00 00
00 00 mov DWORD PTR __$EHRec$[ebp+8], 0
  00030 e8 00 00 00 00 call ?magic@@YAHXZ ; magic
  00035 a3 00 00 00 00 mov DWORD PTR ?m@?1??user@@YAHXZ@4HA, eax



On VS 2015 the generated code (x86, Release) looks like this:

  00018 64 a1 00 00 00
00 mov eax, DWORD PTR fs:__tls_array
  0001e 8b 0d 00 00 00
00 mov ecx, DWORD PTR __tls_index
  00024 8b 0c 88 mov ecx, DWORD PTR [eax+ecx*4]
  00027 a1 00 00 00 00 mov eax, DWORD PTR ?$TSS0@?1??user@@YAHXZ@4HA
  0002c 3b 81 00 00 00
00 cmp eax, DWORD PTR __Init_thread_epoch[ecx]
  00032 7e 34 jle SHORT $LN5@user
  00034 68 00 00 00 00 push OFFSET ?$TSS0@?1??user@@YAHXZ@4HA
  00039 e8 00 00 00 00 call __Init_thread_header
  0003e 83 c4 04 add esp, 4
  00041 83 3d 00 00 00
00 ff cmp DWORD PTR ?$TSS0@?1??user@@YAHXZ@4HA, -1
  00048 75 1e jne SHORT $LN5@user
  0004a c7 45 fc 00 00
00 00 mov DWORD PTR __$EHRec$[ebp+8], 0
  00051 e8 00 00 00 00 call ?magic@@YAHXZ ; magic
  00056 68 00 00 00 00 push OFFSET ?$TSS0@?1??user@@YAHXZ@4HA
  0005b a3 00 00 00 00 mov DWORD PTR ?m@?1??user@@YAHXZ@4HA, eax
  00060 e8 00 00 00 00 call __Init_thread_footer
  00065 83 c4 04 add esp, 4
$LN5@user:

; 210  :   return m;

  00068 a1 00 00 00 00 mov eax, DWORD PTR ?m@?1??user@@YAHXZ@4HA
Reply all
Reply to author
Forward
0 new messages