3

My code is periodically crashing on QNX. It crashes with error

error reading variable: Cannot access memory at address 0x85dd6ac)

while trying to access std::map member variable of 0x85dd6ac object, which is lazy initialized using std::call_once.

Initialization is done with the following pseudo-code:

mutable std::aligned_storage<sizeof(A), alignof(A) >::type m_value;

void init(A *ptr)
{
    new (ptr) A();
}

inline T* data() const
{
    return reinterpret_cast<A*>(&m_value);
}

const A& get() const
{
    std::call_once(m_once_flag, init, data());
    return *data();
}

At some point when object returned by get() is accessed the process is crashed.

On other platforms issue is not reproduced and it's very difficult to debug. From the code I can see that object cannot be uninitialized and it also cannot be deleted at that point.

I suspect there can be issue with std::call_once implementation with thread safety or memory ordering. Does anybody have experience with std::call_once on QNX platform or bugs like that? Any ideas how I can find the issue?

incognito
  • 457
  • 5
  • 19
  • Are you using GCC? Wild guess: the QNX Pthreads implementation only supports using PTHREAD_ONCE_INIT for static objects, and the use for a data member in the `std::once_flag` type doesn't work. Does it make a difference if you use a global `std::once_flag` instead of `m_once_flag`? (I realise that means you can only do the init once, not once per object, but it might help track down the problem). – Jonathan Wakely Mar 22 '16 at 14:59
  • @Jonathan, it's `mutable`, so it must be a non-static data member (which the "m_" prefix also strongly suggests!) – Jonathan Wakely Mar 22 '16 at 15:00
  • why don't store the variable in the map only when it's needed to be stored? I don't get it – David Haim Mar 22 '16 at 15:05
  • I might be out of date here, but last time I checked QNX's supported GCC was version 4.7. C++11 support is a wee bit shaky. – user4581301 Mar 22 '16 at 15:16
  • Does using of low-level POSIX pthread_once fix the issue? – SergeyA Mar 22 '16 at 15:16
  • Thanks for comments! Yes, I am using gcc and yes, it is mutable non-static data member and the class is a template class. I will test with global once_flag and POSIX pthread_once and come back. Thanks once again for ideas. – incognito Mar 22 '16 at 15:22
  • @DavidHaim it is actually more complicated than that. I just simplified to highlight the important part. – incognito Mar 22 '16 at 15:25

2 Answers2

1

Issue was with std::call_once. It is bug in implementation. Temporary replacing with mutex solved the issue. Didn't have more time to dig into details, but hopefully this information will help someone with similar issue.

Thanks everyone for your comments!

incognito
  • 457
  • 5
  • 19
1

I have the same experience by using std::call_once in QNX but beside crash it can cause also dead lock in multi-threading application. I suggest to use the following pattern to replace std::call_once:

static std::atomic< bool > once_flag = false;
if ( !once_flag.exchange( true ) )
{
    // This part will be executed only once.
    // ...
}

UPDATE Based on comment of fefe this solution does not satisfy the condition of blocking of passive execution (see details on http://www.cplusplus.com/reference/mutex/call_once/).

If you have to satify this as well you should implement more complex solution. Here is an example:

static std::atomic< bool > once_flag = false;
static std::atomic< bool > once_call_done = false;
if ( !once_flag.exchange( true ) )
{
    // This part will be executed only once.
    // ...

    once_call_done = true;
}
else
{
    // Block until the call once part is running.
    while( !once_call_done )
    {
        sleep( 1 );
    }
}
Tibor Takács
  • 3,535
  • 1
  • 20
  • 23
  • 1
    The problem is, the code following this block may be executed before the execution of the initialization finishes. As the flag is changed before the initialization finishes, other thread may find `one_flag` is `true` before initialization finishes. – fefe Aug 21 '16 at 13:47
  • Thanks, good point. I will modify my answer based on your comment. – Tibor Takács Aug 21 '16 at 18:32