4

Suppose (for a toy example) that I have the class:

class Foo {
    mutable std::mutex mutex;
    std::vector<int> data;
public:
    void add(int x) {
        std::lock_guard<std::mutex> lock(mutex);
        data.push_back(x);
    }
    std::vector<int> getData() const;
};

Now, I'm sure this is an OK version of getData():

std::vector<int> Foo::getData() const {
    std::lock_guard<std::mutex> lock(mutex);
    auto result = data; // Safely copy.
    return result; // Return a local by value.
} // lock goes out of scope and unlocks.

but what about this? Is this safe?:

std::vector<int> Foo::getData() const {
    std::lock_guard<std::mutex> lock(mutex);
    return data; // <- Is this threadsafe?
}

The return line happens within the lock's lifetime, so maybe it's safe? OTOH, we are copying a member, so data outlives lock. Maybe the lock goes out of scope before data is copied to the caller?

So: Is the second version threadsafe?

Ben
  • 9,184
  • 1
  • 43
  • 56
  • Looking at c++17, with it's guaranteed copy elision, the second version should work just fine. I would assume that it's OK in the previous versions, however given the question I'm doubting – JVApen Dec 17 '18 at 14:32
  • @JVApen I don't think elision affects this, does it? – Lightness Races in Orbit Dec 17 '18 at 14:34
  • @LightnessRacesinOrbit what is this a duplicate of? I couldn't find this question. – Ben Dec 17 '18 at 14:49
  • My concern, I think, is the details of copy elision: If when we return the compiler says "we have `data`, we know where to construct it at the call site, let's tear down the local stack (i.e., the lock) then copy-construct `data` to the caller. – Ben Dec 17 '18 at 14:51
  • 1
    @Ben It lists the duplicates at the top of the page. I've found four for you – Lightness Races in Orbit Dec 17 '18 at 14:59
  • 1
    Don't worry about elision - those low level details about some "stack" have no bearing on the meaning of a program. – Lightness Races in Orbit Dec 17 '18 at 14:59
  • Thanks. I think my intuition comes from the notion of the stack being built and unwound in order, which would make me expect the stack to have [to-be-returned value], (function call) [lock], so I was picturing lock getting destroyed before we mess with [to-be-returned value], but that logic doesn't extend to my feels-safer version: [to-be-returned value], (function call) [lock], [result]. In both cases, [to-be-returned value] is constructed after [lock]. So it sounds like the first version copy-constructs the result at [to-be-returned value] before destroying [lock]. Thanks! – Ben Dec 17 '18 at 15:29

0 Answers0