Mercurial > public > mercurial-scm > hg
comparison mercurial/revset.py @ 20833:47d43e2323c5
revset: fix generatorset race condition
If two things were iterating over a generatorset at the same time, they could
miss out on the things the other was generating, resulting in incomplete
results. This fixes it by making it possible for two things to iterate at once,
by always checking the _genlist at the beginning of each iteration.
I was only able to repro it with pending changes from my other commits, but they
aren't ready yet. So I'm unable to add a test for now.
author | Durham Goode <durham@fb.com> |
---|---|
date | Tue, 25 Mar 2014 16:10:07 -0700 |
parents | 9a09a625bc93 |
children | bc95143446e8 |
comparison
equal
deleted
inserted
replaced
20832:5d57b2101ab1 | 20833:47d43e2323c5 |
---|---|
2622 def __init__(self, gen): | 2622 def __init__(self, gen): |
2623 """ | 2623 """ |
2624 gen: a generator producing the values for the generatorset. | 2624 gen: a generator producing the values for the generatorset. |
2625 """ | 2625 """ |
2626 self._gen = gen | 2626 self._gen = gen |
2627 self._iter = iter(gen) | |
2628 self._cache = {} | 2627 self._cache = {} |
2629 self._genlist = baseset([]) | 2628 self._genlist = baseset([]) |
2630 self._iterated = False | |
2631 self._finished = False | 2629 self._finished = False |
2632 | 2630 |
2633 def __contains__(self, x): | 2631 def __contains__(self, x): |
2634 if x in self._cache: | 2632 if x in self._cache: |
2635 return self._cache[x] | 2633 return self._cache[x] |
2637 # Use new values only, as existing values would be cached. | 2635 # Use new values only, as existing values would be cached. |
2638 for l in self._consumegen(): | 2636 for l in self._consumegen(): |
2639 if l == x: | 2637 if l == x: |
2640 return True | 2638 return True |
2641 | 2639 |
2642 self._finished = True | |
2643 self._cache[x] = False | 2640 self._cache[x] = False |
2644 return False | 2641 return False |
2645 | 2642 |
2646 def __iter__(self): | 2643 def __iter__(self): |
2647 if self._iterated: | 2644 if self._finished: |
2648 # At least a part of the list should be cached if iteration has | 2645 for x in self._genlist: |
2649 # started over the generatorset. | 2646 yield x |
2650 for l in self._genlist: | 2647 return |
2651 yield l | 2648 |
2652 | 2649 i = 0 |
2653 for item in self._consumegen(): | 2650 genlist = self._genlist |
2654 yield item | 2651 consume = self._consumegen() |
2652 while True: | |
2653 if i < len(genlist): | |
2654 yield genlist[i] | |
2655 else: | |
2656 yield consume.next() | |
2657 i += 1 | |
2655 | 2658 |
2656 def _consumegen(self): | 2659 def _consumegen(self): |
2657 self._iterated = True | |
2658 | |
2659 for item in self._gen: | 2660 for item in self._gen: |
2660 self._cache[item] = True | 2661 self._cache[item] = True |
2661 self._genlist.append(item) | 2662 self._genlist.append(item) |
2662 yield item | 2663 yield item |
2663 | |
2664 self._finished = True | 2664 self._finished = True |
2665 | 2665 |
2666 def set(self): | 2666 def set(self): |
2667 return self | 2667 return self |
2668 | 2668 |