Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialization of data member thread and mutex. Does wrong order has Undefined Behavior?

I stumbled upon what I think is a very easy way to unknowingly shoot yourself in the foot.


first a little introduction

The initialization order of data members is the order of declaration of the data members. So this is illegal:

struct A
{
    std::size_t i_;
    std::size_t length_;

    A(std::size_t length)
      : i_{length_} // UB here. `length_` is uninitialized
        length_{length}
    {}
};

Because data member length_ is uninitialized when used in the initializer of i_. Fortunately both gcc and clang give a very nice warning for this. The simple solution is to initialize each data members from the parameters, i.e. i_{length}.


now to the main point

But how about when it is not immediately obvious. E.g. when a data member is a std::thread

struct X
{
    std::thread thread_;
    std::mutex mutex_;

    X() : thread_{&X::worker_thread, this}
    {}

    auto worker_thread() -> void
    {
        // use mutex_  
        std::lock_guard lk{mutex_}; // boom?
        // ..
    }
};

The same situation arises when using data member initializer:

struct X
{
    std::thread thread_{&X::worker_thread, this};
    std::mutex mutex_;
};

This looks very innocent and neither gcc and clang warn on this scenario. This is not surprising, as the dependency is hidden.

I would immagine the above scenario is not uncommon so I am looking on confirmation that this is indeed UB. And declare the std::mutex data member last, or default initialize it and assign it later.

like image 517
bolov Avatar asked Mar 06 '23 01:03

bolov


1 Answers

Yes, it is indeed undefined behavior. As a matter of fact, you have over-complicated the example with threads and mutexes. Every time you are using this in initialization of members (explicitly or implicitly), you are opening yourself to troubles. Easier example:

struct A {
    int y;
    int x = 0;
    A() : y(sety()) { }

    int sety() { return x; } // Ka-boom!
};

It is always quite dangerous to call non-static member functions from within member initialization; and one generally has also to be careful when calling member functions from constructor body.

like image 103
SergeyA Avatar answered Apr 28 '23 02:04

SergeyA