samedi 17 avril 2021

What is the definition of a thread safe class member function according to the C++11 (Language/Library) Standard?

Example 1

Consider the following basic class:

class X {
    int x;
public:
    void set_x (int new_x) {x = new_x;}
    void get_x () const {return x;}
}

Is set_x thread safe?

Off course, set_x is not thread safe as calling it from two threads simultaneously results in a data race.

Is get_x thread safe?

Two possible reasonings exists:

  1. Yes, it is thread safe, as calling get_x from two threads simultaneously does not result in a data race.
  2. No, it is not thread safe, as calling get_x and set_x from two threads simultaneously results in a data race.

Which one is the correct reasoning?

Example 2

Consider the following related class:

class Y {
    int y;
    std::mutex m; // mutex added
public:
    void set_y (int new_y) {
        std::lock_guard<std::mutex> guard(m); // guard setter with mutex
        y = new_y;
    }
    void get_y () const {return y;}
}

Is set_y thread safe?

Two possible reasonings exists:

  1. Yes, it is thread safe, as calling set_y from two threads simultaneously does not result in a data race.
  2. No, it is not thread safe, as calling set_y and get_y from two threads simultaneously results in a data race.

Which one is the correct reasoning?

Is get_y thread safe?

Nothing changed: same options as for get_x.

Correct thread safe class

Off course the correct thread safe implementation is:

class Z {
    int z;
    mutable std::mutex m; // could be replaced by std::shared_mutex in C++17 (or std::shared_timed_mutex in C++14);
public:
    void set_z (int new_z) {
        std::lock_guard<std::mutex> guard(m);
        z = new_z;
    }
    void get_z () const {
        std::lock_guard<std::mutex> guard(m); // guard getter with mutex
        return z;
    }
}

Question summary

Which reasoning is correct?

  1. A function is thread safe iff calling it from two threads simultaneously does not result in a data race. Could work for example 1, but fails in example 2, as this would result to the conclusion that set_y and get_y are thread safe, but class Y isn't as calling set_y and get_y from two threads simultaneously results in a data race.
  2. A function is thread safe iff it does not access any memory that could be modified without internal synchronization by another function. This seems to me the most consistent option, but is not the way it is often used (see related threads).

Related threads

Note that I have read the following related threads:

Aucun commentaire:

Enregistrer un commentaire