Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread-safety with C++ and passing by reference

I wanted to confirm my understanding of threads and passing by reference in C++. Is the following function thread safe?

QString sA = "hello";
QString sB = "world";
bool someFlag = AreStringsEqual(sA,sB);

...

bool AreStringsEqual(QString const &stringA, QString const &stringB)
{
    if(stringA == stringB)
    {   return true;   }

    return false;
}

I think it is thread safe. I'd like it if someone could confirm my thought process, or tell me I have no idea what I'm talking about :)

  • There are two copies of sA and sB in the process's memory. One set is created on Thread1's stack and the second set is created on Thread2's stack. Because we passed by reference, each thread only needs one set of sA and sB in memory to execute the function call.

  • If we had passed by value instead, there could be up to four copies of sA and sB in the process's memory (each thread having two sets) at some time point where both threads were trading processor control within the function call.

  • In no case is memory shared here, therefore the function is thread safe.

Sorry if this question is super simple, threads have fried my brain :)

Pris

like image 820
Prismatic Avatar asked Jan 03 '12 06:01

Prismatic


3 Answers

There's no reason why two threads wouldn't hold references to the same strings.

This function is not thread-safe because the statement if(stringA == stringB) is not atomic. First you fetch stringA from memory, and only then string B.

Let's stay stringA == stringB == 2.

You fetch stringA, then there's a context switch and both stringA and stringB change to 3. Then you fetch stringB. Your function would return false (because 2 != 3) although stringA was equal to stringB all along.

like image 81
Ilya Kogan Avatar answered Sep 19 '22 06:09

Ilya Kogan


Your question is a little vague on where sA and sB are declared. It sounds like they are declared inside a function, in which case you're correct that each thread would have it's own version of sA and sB. But, in the odd chance that they are declared at global scope, this is not the case. If I understand your question correctly, you meant that the two were declared at local scope, so your first point is correct. By the same token, your second point is correct as well.

Your third point is tricky, though. In your particular case, no memory is shared, so your program is a "thread-safe" program (not sure if that's a good way to word it). However, the function AreStringsEqual is not thread-safe. At some point in the future, you (or someone else) could use the function with data that is shared, and the function itself does not guard itself against this usage.

like image 29
Ken Wayne VanderLinde Avatar answered Sep 20 '22 06:09

Ken Wayne VanderLinde


Unless QString has specified that operator== is thread safe, the function is not thread safe. The implementation of AreStringsEqual does nothing itself to protect the data.

You are putting the responsibility of thread safety on the client with this implementation. The client must ensure the parameters and the parameters' internal data does not mutate (e.g. by another thread) while in AreStringsEqual. Consequently, they may find themselves making unnecessary copies. How exactly this must happen is dictated by the implementation of QString. Even std::string implementations vary dramatically =)

For strings in concurrent contexts, one would generally take a copy before moving the string into a concurrent context. If it really needs to be shared, you'll need something to protect it (such as a lock). For primitive collections (e.g. std::string and std::vector), you'll want to avoid locking at every access because it would kill performance and could fail rather easily. So, you'd generally copy or lock if you must share objects which are not explicitly thread safe.

Therefore, the implementation of AreStringsEqual is not thread safe (again, unless bool QString::operator==(const QString&) const is guaranteed to be thread safe).

However, your usage of AreStringsEqual:

QString sA = "hello";
QString sB = "world";
bool someFlag = AreStringsEqual(sA,sB);

would be fine for the majority of string implementations, because the parameters and their data would be local to the thread.

like image 30
justin Avatar answered Sep 23 '22 06:09

justin