8

In using my application, I've stumbled upon a race condition in some code that uses a NSOperationQueue to run tasks asynchronously following user-triggered events. I know how to fix the race condition, since it's a stupid design error that I won't delve into, but I'd like to prove the bug with a test case (so that it doesn't come back during optimizing/refactoring further down the line). This has me stumped. How does one go about testing something that is multi-threaded, especially when the aim of the test is to generate a race condition?

Does anyone have any links to reference material I can refer to when it comes to dealing with threads and unit testing? I'm particularly interested in race condition generation.

d11wtq
  • 34,788
  • 19
  • 120
  • 195
  • I would presume you'd mock any shared data structures, and inside your mock objects you can do any synchronization you need to make different threads execute in the "wrong" order. – Anon. Dec 14 '10 at 03:18

1 Answers1

4

You have to make sure that the sequence of events causing the race condition actually arises during testing. For that, you need to affect the thread interleaving inside the test case.

You can achieve that with additional (conditional) synchronization, or, (simpler and less reliable) additional timers. Put some sleep() calls into the critical sections to make sure they run long enough for the other thread to arrive in the undesirable state. When you have that working, replace the sleep calls with explicit synchronization (i.e. block one thread until the other one actually acknowledges to have arrived).

Then make this all conditional on a global flag that is set by the test case.

Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
  • 3
    I honestly don't think tightly coupling the actual production code to this particular test is a good idea. It means you need to be careful not to upset that coupling whenever you refactor the code, which defeats the point of having this test in the first place. – Anon. Dec 14 '10 at 03:23
  • No, the fragility wrt. to refactoring actually supports this testing approach. When you refactor, you will have to rethink the race conditions. Then, you may find that the refactoring actually introduced new ones, or that the ones it was previously vulnerable to can't possibly happen anymore. – Martin v. Löwis Dec 14 '10 at 03:27
  • I just don't see how this "forces" anyone refactoring to rethink potential race conditions, or that doing so is actually useful. Clearly, thinking about race conditions didn't work the first time. Anyone refactoring the code to any significant extent would either have to throw the existing test code out entirely and write new ones, or render the test code useless. Either way, it defeats the purpose of having test code. Better would be to have test code that doesn't pollute the main codebase but instead flags the build as broken if it is. – Anon. Dec 14 '10 at 03:41
  • 1
    For a race condition, having a test case that detects it is not feasible. The race condition will never show up during testing. – Martin v. Löwis Dec 14 '10 at 03:46