[cfe-dev] once_flag assumes zero-initialized memory

Gordon Henriksen gordonhenriksen at me.com
Thu Dec 20 18:23:30 PST 2012


libc++'s std::once_flag does not function if it is not initialized in zero-initialized memory. Please find a test attached. The C++11 standard specifically calls out dynamic allocation of once_flag in this example from §30.4.4.2:

    // object flag, member function
    class information {
      std::once_flag verified;
      void verifier();
      
    public:
      void verify() { std::call_once(verified,verifier); }
    };

Here's a short example of the nonconforming behavior:

    $ cat call_once-dynamic.cpp
    #include <mutex>
    #include <type_traits>
    #include <cassert>
    
    int main() {
      std::aligned_storage<sizeof(std::once_flag),
                           alignof(std::once_flag)>::type buffer;
      memset(&buffer, 0xff, sizeof(buffer));
      std::once_flag *flag = new ((void *) &buffer) std::once_flag;
      
      bool called = false;
      std::call_once(*flag, [&called]{ called = true; });
      assert(called && "once_flag is incompatible with dynamic allocation!");
      
      flag->~once_flag();
    }
    $ cat Makefile
    CXX = xcrun clang++
    CXXFLAGS = -std=c++11 -stdlib=libc++
    
    .PHONY: execute-test
    execute-test: call_once-dynamic
            @if ./call_once-dynamic; then echo pass; else echo fail; fi
    
    call_once-dynamic: call_once-dynamic.cpp
            xcrun clang++ -std=c++11 -stdlib=libc++ -o $@ $<
    $ make
    xcrun clang++ -std=c++11 -stdlib=libc++ -o call_once-dynamic call_once-dynamic.cpp
    Assertion failed: (called && "once_flag is incompatible with dynamic allocation!"), function main, file call_once-dynamic.cpp, line 13.
    /bin/sh: line 1: 45638 Abort trap: 6           ./call_once-dynamic
    fail

For users, a workaround is as follows. I think this covers most core use cases, but isn't a perfect replacement for std::once_flag.

    class once_flag_libcpp_hack : public std::once_flag {
    public:
      once_flag_libcpp_hack() {
        memset(static_cast<std::once_flag*>(this), 0, sizeof(std::once_flag));
      }
    };
    
    namespace mycorp { typedef once_flag_libcpp_hack once_flag; }

— Gordon
-------------- next part --------------
A non-text attachment was scrubbed...
Name: call_once-dynamic.cpp
Type: application/octet-stream
Size: 484 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20121220/8a30207f/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Makefile
Type: application/octet-stream
Size: 264 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20121220/8a30207f/attachment-0001.obj>


More information about the cfe-dev mailing list