Skip to content
This repository has been archived by the owner on Apr 22, 2020. It is now read-only.

Race condition in TagAlongThread #211

Open
idanarye opened this issue Jan 1, 2020 · 0 comments
Open

Race condition in TagAlongThread #211

idanarye opened this issue Jan 1, 2020 · 0 comments

Comments

@idanarye
Copy link

idanarye commented Jan 1, 2020

TagAlongThread's __call__() starts with:

def __call__(self):
    assert self.__alive, '%s is dead' % self
    # We can't use an iteration that's already started - maybe it's already at a too advanced stage?
    if self._iterating.is_set():
        self._not_iterating.wait()

    self._iteration_trigger.set()  # Signal that we want an iteration

    while not self._iterating.wait(1):  # Wait until an iteration starts
        # It is possible that we missed the loop and _iterating was already
        # cleared. If this is the case, _not_iterating will not be set -
        # and we can use it as a signal to stop waiting for iteration.
        if self._not_iterating.is_set():
            break
    else:
        self._not_iterating.wait()  # Wait until it finishes

    # ...

Which should trigger the _loop():

    def _loop(self):
        while self.__alive:
            self._iteration_trigger.wait()
            self._iteration_trigger.clear()

            # Mark that we are now iterating
            self._not_iterating.clear()
            self._iterating.set()

            try:
                self._last_exception, self._last_result = None, self._func()
            except Exception as e:
                self._last_exception = e

            # Mark that we are no longer iterating
            self._iterating.clear()
            self._not_iterating.set()

            time.sleep(self.minimal_sleep)

        # ...

Now, consider this scenario:

  • __call__() is called.
  • _iterating is not set - so we are not waiting for _not_iterating.
  • _iteration_trigger.set()
  • We start doing while not self._iterating.wait(1):.
  • For some reason, the loop thread does not get scheduled again during the following second - and _iterating.wait(1) returns (with False, of course)
  • _not_iterating is still set (the loop thread was not scheduled, so it was not cleared), so we assume the iteration was finished and stop waiting.

The condition requires that a thread will not get scheduled for a whole second - but I can see that happen in a busy process...

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant