Skip to content

Commit

Permalink
Fix race condition inside recursive strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
Stranger6667 committed Jan 26, 2021
1 parent 06fdabd commit 5d1a3f2
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 2 deletions.
5 changes: 5 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RELEASE_TYPE: patch

This release prevents a race condition inside :func:``~hypothesis.strategies.recursive`` strategies.
The race condition occurs when the same :func:``~hypothesis.strategies.recursive`` strategy is shared among tests
that are running in multiple threads.
20 changes: 18 additions & 2 deletions hypothesis-python/src/hypothesis/strategies/_internal/recursive.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# END HEADER

from contextlib import contextmanager
import threading

from hypothesis.errors import InvalidArgument
from hypothesis.internal.lazyformat import lazyformat
Expand All @@ -29,8 +30,23 @@ class LimitedStrategy(SearchStrategy):
def __init__(self, strategy):
super().__init__()
self.base_strategy = strategy
self.marker = 0
self.currently_capped = False
self.data = threading.local()

@property
def marker(self):
return getattr(self.data, "marker", 0)

@marker.setter
def marker(self, value):
self.data.marker = value

@property
def currently_capped(self):
return getattr(self.data, "currently_capped", False)

@currently_capped.setter
def currently_capped(self, value):
self.data.currently_capped = value

def __repr__(self):
return "LimitedStrategy(%r)" % (self.base_strategy,)
Expand Down
30 changes: 30 additions & 0 deletions hypothesis-python/tests/cover/test_recursive.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# obtain one at https://mozilla.org/MPL/2.0/.
#
# END HEADER
import threading

import pytest

Expand Down Expand Up @@ -77,3 +78,32 @@ def test_can_exclude_branching_with_max_leaves(t):
@given(st.recursive(st.none(), lambda x: st.one_of(x, x)))
def test_issue_1502_regression(s):
pass


def test_thread_save_draw():
shared_strategy = st.recursive(
st.integers(),
lambda s: st.lists(s, max_size=3)
)

errors = []

@given(data=st.data())
def test(data):
try:
data.draw(shared_strategy)
except Exception as exc:
errors.append(exc)

threads = []

for _ in range(2):
threads.append(threading.Thread(target=test))

for thread in threads:
thread.start()

for thread in threads:
thread.join()

assert not errors

0 comments on commit 5d1a3f2

Please sign in to comment.