Solved How to unit test race conditions
-
I think the title says it all. Any idea on the table is welcome.
Background:
I have a class that is not thread safe and I'm trying to write a bridge between this class API and the rest of the world to make it thread safe so the whole point of the class is to eliminate existing race conditions. How do I test I was successful? -
Build your code and tests with compile-time instrumentation like ThreadSanitizer, or use run-time instrumentation e.g. Helgrind
-
Thanks, I think that is the best you can do for "unknown unknowns" but there must be something better for "known unknowns".
Take this simple example (it probably doesn't compile but gives you the idea):
int gloabalInt=0; class ThreadUnsafeClass{ public: int getValue() const {return gloabalInt;} void setValue(int val){gloabalInt=val;} }; class ThreadSafeClass{ ThreadUnsafeClass base; static QMutex safeMutex; public: int getValue() const {QMutexLocker(&safeMutex); return base.getValue();} void setValue(int val) {QMutexLocker(&safeMutex); return base.setValue(val);} };
I know exactly where the race condition is in
ThreadUnsafeClass
, can I do something better than-fsanitize=thread
to test the same race does not happen inThreadSafeClass
? -
You do realize
ThreadSafeClass
isn't thread-safe, right?can I do something better than -fsanitize=thread to test the same race does not happen in ThreadSafeClass?
I can't think of anything specific right now. But just for completeness - you usually follow the data when dealing with concurrent access, so if the original data's access is serialized at all times then it's thread safe. If not, then not. If you have a concrete case and can share it, please do.
-
@kshegunov said in How to unit test race conditions:
You do realize
ThreadSafeClass
isn't thread-safe, right?And for those of us idly following this conversation, that would be because..... ?
-
int gloabalInt=0;
It can be modified outside the context of the class, so while @VRonin's mutex does provide serialization to all accesses made through
ThreadSafeClass
, it does not address the problem in entirety. If the integer was a private field inThreadUnsafeClass
, then all would've been well. -
@kshegunov
Ah, OK, a "language" thing. Yes I understood that, I assumed if everything does go throughThreadSafeClass
. -
Yeah globals are tricky, if it's not in the class in the first place, one could justifiably assume it's supposed to be used globally, hence protecting the access through a mutex in the class can only do so much. If you make sure you don't touch the global outside the class, then you have no problem of course.
-
I think valgrind actually will catch them by checking on multiple memory accesses. I can't remember for sure if it does or not.
What I usually do is just thrown stuff at my threads as fast as possible via a test to try to have the race condition pop up.
AFAIK there is no really easy way to detect them. That's why they are so nasty to deal with.
-
@kshegunov said in How to unit test race conditions:
It can be modified outside the context of the class
Good spot but in this small example I was just assuming everything goes through
ThreadUnsafeClass
@ambershark said in How to unit test race conditions:
just thrown stuff at my threads as fast as possible via a test to try to have the race condition pop up.
That's what I'm doing at the moment but I felt like I was doing a dumb thing because I didn't know the correct solution
Thanks everyone. At least I feel a little less unconfortable by not being able to reliably data-test this