title: How can I reproduce the race conditions in this python code reliably?
body: <p><strong>Context</strong></p>
  
  <p>I recently posted a <a href="https://codereview.stackexchange.com/questions/33113/is-this-kitchen-timer-code-pythonic">timer class for review on Code Review</a>. I'd had a gut feeling there were concurrency bugs as I'd once seen 1 unit test fail, but was unable to reproduce the failure. Hence my post to code review. </p>
  
  <p>I got some great feedback highlighting various race conditions in the code. (I thought) I understood the problem and the solution, but before making any fixes, I wanted to expose the bugs with a unit test. When I tried, I realised it was difficult. Various stack exchange answers suggested I'd have to control the execution of threads to expose the bug(s) and any contrived timing would not necessarily be portable to a different machine. This seemed like a lot of accidental complexity beyond the problem I was trying to solve.</p>
  
  <p>Instead I tried using <a href="https://stackoverflow.com/questions/35470/are-there-any-static-analysis-tools-for-python">the best static analysis (SA) tool for python</a>, PyLint, to see if it'd pick out any of the bugs, but it couldn't. Why could a human find the bugs through code review (essentially SA), but a SA tool could not?</p>
  
  <p>Afraid of trying to <a href="https://stackoverflow.com/questions/3982036/how-can-i-use-valgrind-with-python-c-extensions">get Valgrind working with python</a> (which sounded like yak-shaving), I decided to have a bash at fixing the bugs without reproducing them first. Now I'm in a pickle.</p>
  
  <p>Here's the code now.</p>
  
  <pre><code>from threading import Timer, Lock
  from time import time
  
  class NotRunningError(Exception): pass
  class AlreadyRunningError(Exception): pass
  
  
  class KitchenTimer(object):
      '''
      Loosely models a clockwork kitchen timer with the following differences:
          You can start the timer with arbitrary duration (e.g. 1.2 seconds).
          The timer calls back a given function when time's up.
          Querying the time remaining has 0.1 second accuracy.
      '''
  
      PRECISION_NUM_DECIMAL_PLACES = 1
      RUNNING = "RUNNING"
      STOPPED = "STOPPED"
      TIMEUP  = "TIMEUP"
  
      def __init__(self):
          self._stateLock = Lock()
          with self._stateLock:
              self._state = self.STOPPED
              self._timeRemaining = 0
  
      def start(self, duration=1, whenTimeup=None):
          '''
          Starts the timer to count down from the given duration and call whenTimeup when time's up.
          '''
          with self._stateLock:
              if self.isRunning():
                  raise AlreadyRunningError
              else:
                  self._state = self.RUNNING
                  self.duration = duration
                  self._userWhenTimeup = whenTimeup
                  self._startTime = time()
                  self._timer = Timer(duration, self._whenTimeup)
                  self._timer.start()
  
      def stop(self):
          '''
          Stops the timer, preventing whenTimeup callback.
          '''
          with self._stateLock:
              if self.isRunning():
                  self._timer.cancel()
                  self._state = self.STOPPED
                  self._timeRemaining = self.duration - self._elapsedTime()
              else:
                  raise NotRunningError()
  
      def isRunning(self):
          return self._state == self.RUNNING
  
      def isStopped(self):
          return self._state == self.STOPPED
  
      def isTimeup(self):
          return self._state == self.TIMEUP
  
      @property
      def timeRemaining(self):
          if self.isRunning():
              self._timeRemaining = self.duration - self._elapsedTime()
          return round(self._timeRemaining, self.PRECISION_NUM_DECIMAL_PLACES)
  
      def _whenTimeup(self):
          with self._stateLock:
              self._state = self.TIMEUP
              self._timeRemaining = 0
              if callable(self._userWhenTimeup):
                  self._userWhenTimeup()
  
      def _elapsedTime(self):
          return time() - self._startTime
  </code></pre>
  
  <p><strong>Question</strong></p>
  
  <p>In the context of this code example, how can I expose the race conditions, fix them, and prove they're fixed?</p>
  
  <p><strong>Extra points</strong></p>
  
  <p>extra points for a testing framework suitable for other implementations and problems rather than specifically to this code.</p>
  
  <p><strong>Takeaway</strong></p>
  
  <p>My takeaway is that the technical solution to reproduce the identified race conditions is to control the synchronism of two threads to ensure they execute in the order that will expose a bug. The important point here is that they are <em>already identified</em> race conditions. The best way I've found to identify race conditions is to put your code up for code review and encourage more expert people analyse it.</p>