with:
config_hash: ${{ needs.check_source.outputs.config_hash }}
options: ./configure --config-cache --with-thread-sanitizer --with-pydebug
+ suppressions_path: Tools/tsan/supressions.txt
build_tsan_free_threading:
name: 'Thread sanitizer (free-threading)'
with:
config_hash: ${{ needs.check_source.outputs.config_hash }}
options: ./configure --config-cache --disable-gil --with-thread-sanitizer --with-pydebug
+ suppressions_path: Tools/tsan/suppressions_free_threading.txt
# CIFuzz job based on https://google.github.io/oss-fuzz/getting-started/continuous-integration/
cifuzz:
options:
required: true
type: string
+ suppressions_path:
+ description: 'A repo relative path to the suppressions file'
+ required: true
+ type: string
jobs:
build_tsan_reusable:
sudo sysctl -w vm.mmap_rnd_bits=28
- name: TSAN Option Setup
run: |
- echo "TSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/Tools/tsan/supressions.txt" >> $GITHUB_ENV
+ echo "TSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/${{ inputs.suppressions_path }}" >> $GITHUB_ENV
echo "CC=clang" >> $GITHUB_ENV
echo "CXX=clang++" >> $GITHUB_ENV
- name: Add ccache to PATH
COUNT = 10
+ if support.check_sanitizer(thread=True) and support.Py_GIL_DISABLED:
+ # Reduce iteration count to get acceptable latency
+ NUM_THREADED_ITERATIONS = 1000
+ else:
+ NUM_THREADED_ITERATIONS = 100000
+
def check_len_cycles(self, dict_type, cons):
N = 20
items = [RefCycle() for i in range(N)]
def test_threaded_weak_valued_setdefault(self):
d = weakref.WeakValueDictionary()
with collect_in_thread():
- for i in range(100000):
+ for i in range(self.NUM_THREADED_ITERATIONS):
x = d.setdefault(10, RefCycle())
self.assertIsNot(x, None) # we never put None in there!
del x
def test_threaded_weak_valued_pop(self):
d = weakref.WeakValueDictionary()
with collect_in_thread():
- for i in range(100000):
+ for i in range(self.NUM_THREADED_ITERATIONS):
d[10] = RefCycle()
x = d.pop(10, 10)
self.assertIsNot(x, None) # we never put None in there!
# WeakValueDictionary when collecting from another thread.
d = weakref.WeakValueDictionary()
with collect_in_thread():
- for i in range(200000):
+ for i in range(2 * self.NUM_THREADED_ITERATIONS):
o = RefCycle()
d[10] = o
# o is still alive, so the dict can't be empty
--- /dev/null
+# This file contains suppressions for the free-threaded build. It contains the
+# suppressions for the default build and additional suppressions needed only in
+# the free-threaded build.
+#
+# reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
+
+## Default build suppresssions
+
+race:get_allocator_unlocked
+race:set_allocator_unlocked
+
+## Free-threaded suppressions
+
+race:_add_to_weak_set
+race:_in_weak_set
+race:_mi_heap_delayed_free_partial
+race:_Py_IsImmortal
+race:_Py_IsOwnedByCurrentThread
+race:_PyEval_EvalFrameDefault
+race:_PyFunction_SetVersion
+race:_PyImport_AcquireLock
+race:_PyImport_ReleaseLock
+race:_PyInterpreterState_SetNotRunningMain
+race:_PyInterpreterState_IsRunningMain
+race:_PyObject_GC_IS_SHARED
+race:_PyObject_GC_SET_SHARED
+race:_PyObject_GC_TRACK
+race:_PyType_HasFeature
+race:_PyType_Lookup
+race:assign_version_tag
+race:compare_unicode_unicode
+race:delitem_common
+race:dictkeys_decref
+race:dictkeys_incref
+race:dictresize
+race:gc_collect_main
+race:gc_restore_tid
+race:initialize_new_array
+race:insertdict
+race:lookup_tp_dict
+race:mi_heap_visit_pages
+race:PyMember_GetOne
+race:PyMember_SetOne
+race:new_reference
+race:set_contains_key
+race:set_inheritable
+race:start_the_world
+race:tstate_set_detached
+race:unicode_hash
+race:update_cache
+race:update_cache_gil_disabled
-## reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
+# This file contains suppressions for the default (with GIL) build.
+# reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
race:get_allocator_unlocked
race:set_allocator_unlocked
-race:mi_heap_visit_pages
-race:_mi_heap_delayed_free_partial