[Python-checkins] gh-112536: Add TSAN builds on Github Actions (#116872)

2024-03-16 Thread pitrou
https://github.com/python/cpython/commit/20578a1f68c841a264b72b00591b11ab2fa77b43
commit: 20578a1f68c841a264b72b00591b11ab2fa77b43
branch: main
author: Donghee Na 
committer: pitrou 
date: 2024-03-16T11:10:37+01:00
summary:

gh-112536: Add TSAN builds on Github Actions (#116872)

files:
A .github/workflows/reusable-tsan.yml
M .github/workflows/build.yml
M Lib/test/test_concurrent_futures/util.py
M Lib/test/test_logging.py
M Lib/test/test_threading.py
M Python/thread_pthread.h

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d43b83e830e1fb..e36859e728b67f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -484,6 +484,24 @@ jobs:
 - name: Tests
   run: xvfb-run make test
 
+  build_tsan:
+name: 'Thread sanitizer'
+needs: check_source
+if: needs.check_source.outputs.run_tests == 'true'
+uses: ./.github/workflows/reusable-tsan.yml
+with:
+  config_hash: ${{ needs.check_source.outputs.config_hash }}
+  options: ./configure --config-cache --with-thread-sanitizer 
--with-pydebug
+
+  build_tsan_free_threading:
+name: 'Thread sanitizer (free-threading)'
+needs: check_source
+if: needs.check_source.outputs.run_tests == 'true'
+uses: ./.github/workflows/reusable-tsan.yml
+with:
+  config_hash: ${{ needs.check_source.outputs.config_hash }}
+  options: ./configure --config-cache --disable-gil 
--with-thread-sanitizer --with-pydebug
+
   # CIFuzz job based on 
https://google.github.io/oss-fuzz/getting-started/continuous-integration/
   cifuzz:
 name: CIFuzz
@@ -542,6 +560,8 @@ jobs:
 - build_windows_free_threading
 - test_hypothesis
 - build_asan
+- build_tsan
+- build_tsan_free_threading
 - cifuzz
 
 runs-on: ubuntu-latest
@@ -575,6 +595,8 @@ jobs:
 build_windows,
 build_windows_free_threading,
 build_asan,
+build_tsan,
+build_tsan_free_threading,
 '
 || ''
   }}
diff --git a/.github/workflows/reusable-tsan.yml 
b/.github/workflows/reusable-tsan.yml
new file mode 100644
index 00..96a9c1b0cda3c3
--- /dev/null
+++ b/.github/workflows/reusable-tsan.yml
@@ -0,0 +1,51 @@
+on:
+  workflow_call:
+inputs:
+  config_hash:
+required: true
+type: string
+  options:
+required: true
+type: string
+
+jobs:
+  build_tsan_reusable:
+name: 'Thread sanitizer'
+runs-on: ubuntu-22.04
+timeout-minutes: 60
+steps:
+- uses: actions/checkout@v4
+- name: Runner image version
+  run: echo "IMAGE_VERSION=${ImageVersion}" >> $GITHUB_ENV
+- name: Restore config.cache
+  uses: actions/cache@v4
+  with:
+path: config.cache
+key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ 
inputs.config_hash }}
+- name: Install Dependencies
+  run: |
+sudo ./.github/workflows/posix-deps-apt.sh
+sudo apt install -y clang
+# Reduce ASLR to avoid TSAN crashing
+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 "CC=clang" >> $GITHUB_ENV
+echo "CXX=clang++" >> $GITHUB_ENV
+- name: Add ccache to PATH
+  run: |
+echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV
+- name: Configure ccache action
+  uses: hendrikmuhs/ccache-action@v1.2
+  with:
+save: ${{ github.event_name == 'push' }}
+max-size: "200M"
+- name: Configure CPython
+  run: ${{ inputs.options }}
+- name: Build CPython
+  run: make -j4
+- name: Display build info
+  run: make pythoninfo
+- name: Tests
+  run: ./python -m test --tsan -j4
diff --git a/Lib/test/test_concurrent_futures/util.py 
b/Lib/test/test_concurrent_futures/util.py
index 3e855031913042..3b8ec3e205d5aa 100644
--- a/Lib/test/test_concurrent_futures/util.py
+++ b/Lib/test/test_concurrent_futures/util.py
@@ -85,6 +85,8 @@ def get_context(self):
 self.skipTest("ProcessPoolExecutor unavailable on this system")
 if sys.platform == "win32":
 self.skipTest("require unix system")
+if support.check_sanitizer(thread=True):
+self.skipTest("TSAN doesn't support threads after fork")
 return super().get_context()
 
 
@@ -111,6 +113,8 @@ def get_context(self):
 self.skipTest("ProcessPoolExecutor unavailable on this system")
 if sys.platform == "win32":
 self.skipTest("require unix system")
+if support.check_sanitizer(thread=True):
+self.skipTest("TSAN doesn't suppor

[Python-checkins] gh-112536: Add test_threading to TSAN tests (#116898)

2024-03-16 Thread pitrou
https://github.com/python/cpython/commit/86bc40dd414bceb3f93382cc9f670936de9d68be
commit: 86bc40dd414bceb3f93382cc9f670936de9d68be
branch: main
author: Antoine Pitrou 
committer: pitrou 
date: 2024-03-16T11:55:46Z
summary:

gh-112536: Add test_threading to TSAN tests (#116898)

files:
M Lib/test/libregrtest/tsan.py
M Lib/test/support/script_helper.py
M Lib/test/test_threading.py

diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py
index 150fcec8816179..fde8ba937c0e4b 100644
--- a/Lib/test/libregrtest/tsan.py
+++ b/Lib/test/libregrtest/tsan.py
@@ -14,6 +14,7 @@
 'test_syslog',
 'test_thread',
 'test_threadedtempfile',
+'test_threading',
 'test_threading_local',
 'test_threadsignals',
 ]
diff --git a/Lib/test/support/script_helper.py 
b/Lib/test/support/script_helper.py
index 759020c33aa700..65e0bc199e7f0b 100644
--- a/Lib/test/support/script_helper.py
+++ b/Lib/test/support/script_helper.py
@@ -63,8 +63,8 @@ class 
_PythonRunResult(collections.namedtuple("_PythonRunResult",
 """Helper for reporting Python subprocess run results"""
 def fail(self, cmd_line):
 """Provide helpful details about failed subcommand runs"""
-# Limit to 80 lines to ASCII characters
-maxlen = 80 * 100
+# Limit to 300 lines of ASCII characters
+maxlen = 300 * 100
 out, err = self.out, self.err
 if len(out) > maxlen:
 out = b'(... truncated stdout ...)' + out[-maxlen:]
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 9769cb41e3e689..886866626dc9f8 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -47,14 +47,11 @@ def skip_unless_reliable_fork(test):
 return unittest.skip("due to known OS bug related to 
thread+fork")(test)
 if support.HAVE_ASAN_FORK_BUG:
 return unittest.skip("libasan has a pthread_create() dead lock related 
to thread+fork")(test)
+if support.check_sanitizer(thread=True):
+return unittest.skip("TSAN doesn't support threads after fork")
 return test
 
 
-skip_if_tsan_fork = unittest.skipIf(
-support.check_sanitizer(thread=True),
-"TSAN doesn't support threads after fork")
-
-
 def requires_subinterpreters(meth):
 """Decorator to skip a test if subinterpreters are not supported."""
 return unittest.skipIf(interpreters is None,
@@ -428,6 +425,10 @@ def test_finalize_running_thread(self):
 # Issue 1402: the PyGILState_Ensure / _Release functions may be called
 # very late on python exit: on deallocation of a running thread for
 # example.
+if support.check_sanitizer(thread=True):
+# the thread running `time.sleep(100)` below will still be alive
+# at process exit
+self.skipTest("TSAN would report thread leak")
 import_module("ctypes")
 
 rc, out, err = assert_python_failure("-c", """if 1:
@@ -460,6 +461,11 @@ def waitingThread():
 def test_finalize_with_trace(self):
 # Issue1733757
 # Avoid a deadlock when sys.settrace steps into threading._shutdown
+if support.check_sanitizer(thread=True):
+# the thread running `time.sleep(2)` below will still be alive
+# at process exit
+self.skipTest("TSAN would report thread leak")
+
 assert_python_ok("-c", """if 1:
 import sys, threading
 
@@ -639,7 +645,6 @@ def test_daemon_param(self):
 self.assertTrue(t.daemon)
 
 @skip_unless_reliable_fork
-@skip_if_tsan_fork
 def test_dummy_thread_after_fork(self):
 # Issue #14308: a dummy thread in the active list doesn't mess up
 # the after-fork mechanism.
@@ -709,7 +714,6 @@ def f():
 
 @skip_unless_reliable_fork
 @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
-@skip_if_tsan_fork
 def test_main_thread_after_fork(self):
 code = """if 1:
 import os, threading
@@ -1278,7 +1282,6 @@ def test_2_join_in_forked_process(self):
 self._run_and_join(script)
 
 @skip_unless_reliable_fork
-@skip_if_tsan_fork
 def test_3_join_in_forked_from_thread(self):
 # Like the test above, but fork() was called from a worker thread
 # In the forked process, the main Thread object must be marked as 
stopped.
@@ -1311,6 +1314,11 @@ def test_4_daemon_threads(self):
 # Check that a daemon thread cannot crash the interpreter on shutdown
 # by manipulating internal structures that are being disposed of in
 # the main thread.
+if support.check_sanitizer(thread=True):
+# some o

[Python-checkins] gh-114271: Fix race in `Thread.join()` (#114839)

2024-03-16 Thread pitrou
https://github.com/python/cpython/commit/33da0e844c922b3dcded75fbb9b7be67cb013a17
commit: 33da0e844c922b3dcded75fbb9b7be67cb013a17
branch: main
author: mpage 
committer: pitrou 
date: 2024-03-16T13:56:30+01:00
summary:

gh-114271: Fix race in `Thread.join()` (#114839)

There is a race between when `Thread._tstate_lock` is released[^1] in 
`Thread._wait_for_tstate_lock()`
and when `Thread._stop()` asserts[^2] that it is unlocked. Consider the 
following execution
involving threads A, B, and C:

1. A starts.
2. B joins A, blocking on its `_tstate_lock`.
3. C joins A, blocking on its `_tstate_lock`.
4. A finishes and releases its `_tstate_lock`.
5. B acquires A's `_tstate_lock` in `_wait_for_tstate_lock()`, releases it, but 
is swapped
   out before calling `_stop()`.
6. C is scheduled, acquires A's `_tstate_lock` in `_wait_for_tstate_lock()` but 
is swapped
   out before releasing it.
7. B is scheduled, calls `_stop()`, which asserts that A's `_tstate_lock` is 
not held.
   However, C holds it, so the assertion fails.

The race can be reproduced[^3] by inserting sleeps at the appropriate points in
the threading code. To do so, run the `repro_join_race.py` from the linked repo.

There are two main parts to this PR:

1. `_tstate_lock` is replaced with an event that is attached to `PyThreadState`.
   The event is set by the runtime prior to the thread being cleared (in the 
same
   place that `_tstate_lock` was released). `Thread.join()` blocks waiting for 
the
   event to be set.
2. `_PyInterpreterState_WaitForThreads()` provides the ability to wait for all
   non-daemon threads to exit. To do so, an `is_daemon` predicate was added to
   `PyThreadState`. This field is set each time a thread is created. 
`threading._shutdown()`
   now calls into `_PyInterpreterState_WaitForThreads()` instead of waiting on
   `_tstate_lock`s.

[^1]: 
https://github.com/python/cpython/blob/441affc9e7f419ef0b68f734505fa2f79fe653c7/Lib/threading.py#L1201
[^2]: 
https://github.com/python/cpython/blob/441affc9e7f419ef0b68f734505fa2f79fe653c7/Lib/threading.py#L1115
[^3]: 
https://github.com/mpage/cpython/commit/81946532792f938cd6f6ab4c4ff92a4edf61314f

-

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Antoine Pitrou 

files:
A Misc/NEWS.d/next/Library/2024-02-01-03-09-38.gh-issue-114271.raCkt5.rst
M Include/cpython/pystate.h
M Include/internal/pycore_lock.h
M Include/internal/pycore_pythread.h
M Lib/test/test_audit.py
M Lib/test/test_concurrent_futures/test_process_pool.py
M Lib/test/test_thread.py
M Lib/test/test_threading.py
M Lib/threading.py
M Modules/_threadmodule.c
M Python/lock.c
M Python/pystate.c

diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h
index ac7ff83748dbfc..38d0897ea13161 100644
--- a/Include/cpython/pystate.h
+++ b/Include/cpython/pystate.h
@@ -161,32 +161,6 @@ struct _ts {
  */
 uintptr_t critical_section;
 
-/* Called when a thread state is deleted normally, but not when it
- * is destroyed after fork().
- * Pain:  to prevent rare but fatal shutdown errors (issue 18808),
- * Thread.join() must wait for the join'ed thread's tstate to be unlinked
- * from the tstate chain.  That happens at the end of a thread's life,
- * in pystate.c.
- * The obvious way doesn't quite work:  create a lock which the tstate
- * unlinking code releases, and have Thread.join() wait to acquire that
- * lock.  The problem is that we _are_ at the end of the thread's life:
- * if the thread holds the last reference to the lock, decref'ing the
- * lock will delete the lock, and that may trigger arbitrary Python code
- * if there's a weakref, with a callback, to the lock.  But by this time
- * _PyRuntime.gilstate.tstate_current is already NULL, so only the simplest
- * of C code can be allowed to run (in particular it must not be possible 
to
- * release the GIL).
- * So instead of holding the lock directly, the tstate holds a weakref to
- * the lock:  that's the value of on_delete_data below.  Decref'ing a
- * weakref is harmless.
- * on_delete points to _threadmodule.c's static release_sentinel() 
function.
- * After the tstate is unlinked, release_sentinel is called with the
- * weakref-to-lock (on_delete_data) argument, and release_sentinel releases
- * the indirectly held lock.
- */
-void (*on_delete)(void *);
-void *on_delete_data;
-
 int coroutine_origin_tracking_depth;
 
 PyObject *async_gen_firstiter;
diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h
index 971b4611a87f3b..f993c95ecbf75a 100644
--- a/Include/internal/pycore_lock.h
+++ b/Include/internal/pycore_lock.h
@@ -153,16 +153,6 @@ PyAPI_FUNC(void) PyEvent_Wait(PyEvent *evt);
 // and 0 if the timeout expired or thread was interrupted.
 PyAPI_FUNC(int) PyEvent_WaitTimed(PyEvent *evt, PyTime_t t

[Python-checkins] gh-112536: Add more TSAN tests (#116896)

2024-03-16 Thread pitrou
https://github.com/python/cpython/commit/bee7e290cdedb17e06f473a2f318c720ba766852
commit: bee7e290cdedb17e06f473a2f318c720ba766852
branch: main
author: Donghee Na 
committer: pitrou 
date: 2024-03-16T14:52:44Z
summary:

gh-112536: Add more TSAN tests (#116896)


-

Co-authored-by: Antoine Pitrou 

files:
M Lib/test/libregrtest/tsan.py

diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py
index fde8ba937c0e4b..c5aed436b829d1 100644
--- a/Lib/test/libregrtest/tsan.py
+++ b/Lib/test/libregrtest/tsan.py
@@ -10,6 +10,7 @@
 'test_importlib',
 'test_io',
 'test_logging',
+'test_queue',
 'test_ssl',
 'test_syslog',
 'test_thread',
@@ -17,6 +18,7 @@
 'test_threading',
 'test_threading_local',
 'test_threadsignals',
+'test_weakref',
 ]
 
 

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] GH-112536: Add more TSan tests (#116911)

2024-03-17 Thread pitrou
https://github.com/python/cpython/commit/b8d808ddd77f84de9f93adcc2aede2879eb5241e
commit: b8d808ddd77f84de9f93adcc2aede2879eb5241e
branch: main
author: Antoine Pitrou 
committer: pitrou 
date: 2024-03-17T09:47:14+01:00
summary:

GH-112536: Add more TSan tests (#116911)

These may all exercise some non-trivial aspects of thread synchronization.

files:
M Lib/test/libregrtest/tsan.py
M Modules/_testinternalcapi.c

diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py
index c5aed436b829d1..dd18ae2584f5d8 100644
--- a/Lib/test/libregrtest/tsan.py
+++ b/Lib/test/libregrtest/tsan.py
@@ -2,6 +2,9 @@
 # chosen because they use threads and run in a reasonable amount of time.
 
 TSAN_TESTS = [
+# TODO: enable more of test_capi once bugs are fixed (GH-116908, 
GH-116909).
+'test_capi.test_mem',
+'test_capi.test_pyatomic',
 'test_code',
 'test_enum',
 'test_functools',
@@ -11,6 +14,9 @@
 'test_io',
 'test_logging',
 'test_queue',
+'test_signal',
+'test_socket',
+'test_sqlite3',
 'test_ssl',
 'test_syslog',
 'test_thread',
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index b3076a8f548b62..1c10dd02138f3a 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -1287,8 +1287,8 @@ check_pyobject_forbidden_bytes_is_freed(PyObject *self,
 static PyObject *
 check_pyobject_freed_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
 {
-/* This test would fail if run with the address sanitizer */
-#ifdef _Py_ADDRESS_SANITIZER
+/* ASan or TSan would report an use-after-free error */
+#if defined(_Py_ADDRESS_SANITIZER) || defined(_Py_THREAD_SANITIZER)
 Py_RETURN_NONE;
 #else
 PyObject *op = PyObject_CallNoArgs((PyObject *)&PyBaseObject_Type);

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] [3.12] gh-112536: Add support for thread sanitizer (TSAN) (gh-112648) (#116924)

2024-03-17 Thread pitrou
https://github.com/python/cpython/commit/2ac1b48a044429d7a290310348b53a87b9f2033a
commit: 2ac1b48a044429d7a290310348b53a87b9f2033a
branch: 3.12
author: Antoine Pitrou 
committer: pitrou 
date: 2024-03-17T16:33:35+01:00
summary:

[3.12] gh-112536: Add support for thread sanitizer (TSAN) (gh-112648) (#116924)

* [3.12] gh-112536: Add support for thread sanitizer (TSAN) (gh-112648)
(cherry picked from commit 88cb9720001295f82c7771ab4ebf20f3cd0b31fb)

* Remove doc for configure option (leave it hidden in this branch)

-

Co-authored-by: Samet YASLAN 

files:
A Misc/NEWS.d/next/Build/2023-12-17-18-23-02.gh-issue-112536.8lr3Ep.rst
M Include/pyport.h
M Lib/test/libregrtest/utils.py
M Lib/test/support/__init__.py
M Lib/test/test_io.py
M configure
M configure.ac

diff --git a/Include/pyport.h b/Include/pyport.h
index 35eca7234ca094..30b9c8ebc409f0 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -748,6 +748,11 @@ extern char * _getpty(int *, int, mode_t, int);
 #  define _Py_ADDRESS_SANITIZER
 #endif
 #  endif
+#  if __has_feature(thread_sanitizer)
+#if !defined(_Py_THREAD_SANITIZER)
+#  define _Py_THREAD_SANITIZER
+#endif
+#  endif
 #elif defined(__GNUC__)
 #  if defined(__SANITIZE_ADDRESS__)
 #define _Py_ADDRESS_SANITIZER
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index 1be5abd8828be8..25017e8717f47c 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -349,6 +349,9 @@ def get_build_info():
 # --with-undefined-behavior-sanitizer
 if support.check_sanitizer(ub=True):
 sanitizers.append("UBSAN")
+# --with-thread-sanitizer
+if support.check_sanitizer(thread=True):
+sanitizers.append("TSAN")
 if sanitizers:
 build.append('+'.join(sanitizers))
 
@@ -649,6 +652,7 @@ def display_header(use_resources: tuple[str, ...],
 asan = support.check_sanitizer(address=True)
 msan = support.check_sanitizer(memory=True)
 ubsan = support.check_sanitizer(ub=True)
+tsan = support.check_sanitizer(thread=True)
 sanitizers = []
 if asan:
 sanitizers.append("address")
@@ -656,12 +660,15 @@ def display_header(use_resources: tuple[str, ...],
 sanitizers.append("memory")
 if ubsan:
 sanitizers.append("undefined behavior")
+if tsan:
+sanitizers.append("thread")
 if sanitizers:
 print(f"== sanitizers: {', '.join(sanitizers)}")
 for sanitizer, env_var in (
 (asan, "ASAN_OPTIONS"),
 (msan, "MSAN_OPTIONS"),
 (ubsan, "UBSAN_OPTIONS"),
+(tsan, "TSAN_OPTIONS"),
 ):
 options= os.environ.get(env_var)
 if sanitizer and options is not None:
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index cb5a84aa74e05f..4e793f154940e3 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -391,10 +391,10 @@ def skip_if_buildbot(reason=None):
 isbuildbot = False
 return unittest.skipIf(isbuildbot, reason)
 
-def check_sanitizer(*, address=False, memory=False, ub=False):
+def check_sanitizer(*, address=False, memory=False, ub=False, thread=False):
 """Returns True if Python is compiled with sanitizer support"""
-if not (address or memory or ub):
-raise ValueError('At least one of address, memory, or ub must be True')
+if not (address or memory or ub or thread):
+raise ValueError('At least one of address, memory, ub or thread must 
be True')
 
 
 cflags = sysconfig.get_config_var('CFLAGS') or ''
@@ -411,18 +411,23 @@ def check_sanitizer(*, address=False, memory=False, 
ub=False):
 '-fsanitize=undefined' in cflags or
 '--with-undefined-behavior-sanitizer' in config_args
 )
+thread_sanitizer = (
+'-fsanitize=thread' in cflags or
+'--with-thread-sanitizer' in config_args
+)
 return (
 (memory and memory_sanitizer) or
 (address and address_sanitizer) or
-(ub and ub_sanitizer)
+(ub and ub_sanitizer) or
+(thread and thread_sanitizer)
 )
 
 
-def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False):
+def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False, 
thread=False):
 """Decorator raising SkipTest if running with a sanitizer active."""
 if not reason:
 reason = 'not working with sanitizers active'
-skip = check_sanitizer(address=address, memory=memory, ub=ub)
+skip = check_sanitizer(address=address, memory=memory, ub=ub, 
thread=thread)
 return unittest.skipIf(skip, reason)
 
 # gh-89363: True if fork() can hang if Python is built wi

[Python-checkins] [3.12] gh-112536: Add --tsan test for reasonable TSAN execution times. (gh-116601) (#116929)

2024-03-18 Thread pitrou
https://github.com/python/cpython/commit/fcb230180faff828aa67ca2ee1aa94fc522e6daa
commit: fcb230180faff828aa67ca2ee1aa94fc522e6daa
branch: 3.12
author: Antoine Pitrou 
committer: pitrou 
date: 2024-03-18T10:22:19+01:00
summary:

[3.12] gh-112536: Add --tsan test for reasonable TSAN execution times. 
(gh-116601) (#116929)

(cherry picked from commit ebf29b3)

Co-authored-by: Donghee Na 

files:
A Lib/test/libregrtest/tsan.py
A Misc/NEWS.d/next/Tests/2024-03-11-23-20-28.gh-issue-112536.Qv1RrX.rst
A Tools/tsan/supressions.txt
M Lib/test/libregrtest/cmdline.py
M Lib/test/libregrtest/main.py
M Lib/test/support/script_helper.py
M Lib/test/test_threading.py
M Modules/_testcapi/mem.c

diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py
index 23ca3565662146..ccf5c2e7d77184 100644
--- a/Lib/test/libregrtest/cmdline.py
+++ b/Lib/test/libregrtest/cmdline.py
@@ -164,6 +164,7 @@ def __init__(self, **kwargs) -> None:
 self.match_tests: TestFilter = []
 self.pgo = False
 self.pgo_extended = False
+self.tsan = False
 self.worker_json = None
 self.start = None
 self.timeout = None
@@ -333,6 +334,8 @@ def _create_parser():
help='enable Profile Guided Optimization (PGO) 
training')
 group.add_argument('--pgo-extended', action='store_true',
help='enable extended PGO training (slower training)')
+group.add_argument('--tsan', dest='tsan', action='store_true',
+   help='run a subset of test cases that are proper for 
the TSAN test')
 group.add_argument('--fail-env-changed', action='store_true',
help='if a test file alters the environment, mark '
 'the test as failed')
diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py
index a9725fa967370a..1dcf7474163787 100644
--- a/Lib/test/libregrtest/main.py
+++ b/Lib/test/libregrtest/main.py
@@ -18,6 +18,7 @@
 from .runtests import RunTests, HuntRefleak
 from .setup import setup_process, setup_test_dir
 from .single import run_single_test, PROGRESS_MIN_TIME
+from .tsan import setup_tsan_tests
 from .utils import (
 StrPath, StrJSON, TestName, TestList, TestTuple, TestFilter,
 strip_py_suffix, count, format_duration,
@@ -56,6 +57,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = 
False):
 self.quiet: bool = ns.quiet
 self.pgo: bool = ns.pgo
 self.pgo_extended: bool = ns.pgo_extended
+self.tsan: bool = ns.tsan
 
 # Test results
 self.results: TestResults = TestResults()
@@ -182,6 +184,9 @@ def find_tests(self, tests: TestList | None = None) -> 
tuple[TestTuple, TestList
 # add default PGO tests if no tests are specified
 setup_pgo_tests(self.cmdline_args, self.pgo_extended)
 
+if self.tsan:
+setup_tsan_tests(self.cmdline_args)
+
 exclude_tests = set()
 if self.exclude:
 for arg in self.cmdline_args:
diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py
new file mode 100644
index 00..99cef88e6801a3
--- /dev/null
+++ b/Lib/test/libregrtest/tsan.py
@@ -0,0 +1,31 @@
+# Set of tests run by default if --tsan is specified.  The tests below were
+# chosen because they use threads and run in a reasonable amount of time.
+
+TSAN_TESTS = [
+'test_capi',
+'test_code',
+'test_enum',
+'test_functools',
+'test_httpservers',
+'test_imaplib',
+'test_importlib',
+'test_io',
+'test_logging',
+'test_queue',
+'test_signal',
+'test_socket',
+'test_sqlite3',
+'test_ssl',
+'test_syslog',
+'test_thread',
+'test_threadedtempfile',
+'test_threading',
+'test_threading_local',
+'test_threadsignals',
+'test_weakref',
+]
+
+
+def setup_tsan_tests(cmdline_args):
+if not cmdline_args:
+cmdline_args[:] = TSAN_TESTS[:]
diff --git a/Lib/test/support/script_helper.py 
b/Lib/test/support/script_helper.py
index c2b43f4060eb55..565f3b54a04be5 100644
--- a/Lib/test/support/script_helper.py
+++ b/Lib/test/support/script_helper.py
@@ -64,8 +64,8 @@ class 
_PythonRunResult(collections.namedtuple("_PythonRunResult",
 """Helper for reporting Python subprocess run results"""
 def fail(self, cmd_line):
 """Provide helpful details about failed subcommand runs"""
-# Limit to 80 lines to ASCII characters
-maxlen = 80 * 100
+# Limit to 300 lines of ASCII characters
+maxlen = 300 * 100
 out,

[Python-checkins] [3.12] gh-112536: Add TSAN build on Github Actions (GH-116872)

2024-03-18 Thread pitrou
https://github.com/python/cpython/commit/25243b1461e524560639ebe54bab9b689b6cc31e
commit: 25243b1461e524560639ebe54bab9b689b6cc31e
branch: 3.12
author: Antoine Pitrou 
committer: pitrou 
date: 2024-03-18T09:52:54Z
summary:

[3.12] gh-112536: Add TSAN build on Github Actions (GH-116872)

(cherry picked from commit 20578a1f68c841a264b72b00591b11ab2fa77b43)

Co-authored-by: Donghee Na 

files:
A .github/workflows/reusable-tsan.yml
M .github/workflows/build.yml
M Lib/test/test_concurrent_futures/util.py
M Lib/test/test_logging.py
M Python/thread_pthread.h

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d9c399476e24a6..cd56c7d84ab8f1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -471,6 +471,15 @@ jobs:
 - name: Tests
   run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu"
 
+  build_tsan:
+name: 'Thread sanitizer'
+needs: check_source
+if: needs.check_source.outputs.run_tests == 'true'
+uses: ./.github/workflows/reusable-tsan.yml
+with:
+  config_hash: ${{ needs.check_source.outputs.config_hash }}
+  options: ./configure --config-cache --with-thread-sanitizer 
--with-pydebug
+
   all-required-green:  # This job does nothing and is only used for the branch 
protection
 name: All required checks pass
 if: always()
@@ -485,6 +494,7 @@ jobs:
 - build_windows
 - test_hypothesis
 - build_asan
+- build_tsan
 
 runs-on: ubuntu-latest
 
@@ -513,6 +523,7 @@ jobs:
 build_ubuntu_ssltests,
 build_windows,
 build_asan,
+build_tsan,
 '
 || ''
   }}
diff --git a/.github/workflows/reusable-tsan.yml 
b/.github/workflows/reusable-tsan.yml
new file mode 100644
index 00..96a9c1b0cda3c3
--- /dev/null
+++ b/.github/workflows/reusable-tsan.yml
@@ -0,0 +1,51 @@
+on:
+  workflow_call:
+inputs:
+  config_hash:
+required: true
+type: string
+  options:
+required: true
+type: string
+
+jobs:
+  build_tsan_reusable:
+name: 'Thread sanitizer'
+runs-on: ubuntu-22.04
+timeout-minutes: 60
+steps:
+- uses: actions/checkout@v4
+- name: Runner image version
+  run: echo "IMAGE_VERSION=${ImageVersion}" >> $GITHUB_ENV
+- name: Restore config.cache
+  uses: actions/cache@v4
+  with:
+path: config.cache
+key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ 
inputs.config_hash }}
+- name: Install Dependencies
+  run: |
+sudo ./.github/workflows/posix-deps-apt.sh
+sudo apt install -y clang
+# Reduce ASLR to avoid TSAN crashing
+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 "CC=clang" >> $GITHUB_ENV
+echo "CXX=clang++" >> $GITHUB_ENV
+- name: Add ccache to PATH
+  run: |
+echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV
+- name: Configure ccache action
+  uses: hendrikmuhs/ccache-action@v1.2
+  with:
+save: ${{ github.event_name == 'push' }}
+max-size: "200M"
+- name: Configure CPython
+  run: ${{ inputs.options }}
+- name: Build CPython
+  run: make -j4
+- name: Display build info
+  run: make pythoninfo
+- name: Tests
+  run: ./python -m test --tsan -j4
diff --git a/Lib/test/test_concurrent_futures/util.py 
b/Lib/test/test_concurrent_futures/util.py
index dc48bec796b87f..fc6030e375fb6b 100644
--- a/Lib/test/test_concurrent_futures/util.py
+++ b/Lib/test/test_concurrent_futures/util.py
@@ -85,6 +85,8 @@ def get_context(self):
 self.skipTest("ProcessPoolExecutor unavailable on this system")
 if sys.platform == "win32":
 self.skipTest("require unix system")
+if support.check_sanitizer(thread=True):
+self.skipTest("TSAN doesn't support threads after fork")
 return super().get_context()
 
 
@@ -111,6 +113,8 @@ def get_context(self):
 self.skipTest("ProcessPoolExecutor unavailable on this system")
 if sys.platform == "win32":
 self.skipTest("require unix system")
+if support.check_sanitizer(thread=True):
+self.skipTest("TSAN doesn't support threads after fork")
 return super().get_context()
 
 
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 463bbc575c7275..6bff48c39df7b8 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -80,6 +80,9 @@
 skip_if_asan_fork = unittest.skipIf(
 support.HAVE_ASAN_FORK_BUG,
 "libasan has a pthread_create() de

[Python-checkins] gh-125541: Make Ctrl-C interrupt `threading.Lock.acquire()` on Windows (#125546)

2024-10-17 Thread pitrou
https://github.com/python/cpython/commit/d8c864816121547338efa43c56e3f75ead98a924
commit: d8c864816121547338efa43c56e3f75ead98a924
branch: main
author: Sam Gross 
committer: pitrou 
date: 2024-10-17T20:10:55+02:00
summary:

gh-125541: Make Ctrl-C interrupt `threading.Lock.acquire()` on Windows (#125546)

files:
A Misc/NEWS.d/next/Library/2024-10-15-16-50-03.gh-issue-125541.FfhmWo.rst
M Doc/library/_thread.rst
M Doc/library/threading.rst
M Python/parking_lot.c

diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst
index 6a66fc4c64bc45..ed29ac70035597 100644
--- a/Doc/library/_thread.rst
+++ b/Doc/library/_thread.rst
@@ -187,6 +187,9 @@ Lock objects have the following methods:
.. versionchanged:: 3.2
   Lock acquires can now be interrupted by signals on POSIX.
 
+   .. versionchanged:: 3.14
+  Lock acquires can now be interrupted by signals on Windows.
+
 
 .. method:: lock.release()
 
@@ -219,12 +222,6 @@ In addition to these methods, lock objects can also be 
used via the
 * Calling :func:`sys.exit` or raising the :exc:`SystemExit` exception is
   equivalent to calling :func:`_thread.exit`.
 
-* It is platform-dependent whether the :meth:`~threading.Lock.acquire` method
-  on a lock can be interrupted (so that the :exc:`KeyboardInterrupt` exception
-  will happen immediately, rather than only after the lock has been acquired or
-  the operation has timed out). It can be interrupted on POSIX, but not on
-  Windows.
-
 * When the main thread exits, it is system defined whether the other threads
   survive.  On most systems, they are killed without executing
   :keyword:`try` ... :keyword:`finally` clauses or executing object
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index cb82fea377697b..d4b343db36efb3 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -567,6 +567,9 @@ All methods are executed atomically.
  Lock acquisition can now be interrupted by signals on POSIX if the
  underlying threading implementation supports it.
 
+  .. versionchanged:: 3.14
+ Lock acquisition can now be interrupted by signals on Windows.
+
 
.. method:: release()
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-10-15-16-50-03.gh-issue-125541.FfhmWo.rst 
b/Misc/NEWS.d/next/Library/2024-10-15-16-50-03.gh-issue-125541.FfhmWo.rst
new file mode 100644
index 00..7a20bca1739869
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-15-16-50-03.gh-issue-125541.FfhmWo.rst
@@ -0,0 +1,4 @@
+Pressing :kbd:`Ctrl-C` while blocked in :meth:`threading.Lock.acquire`,
+:meth:`threading.RLock.acquire`, and :meth:`threading.Thread.join` now
+interrupts the function call and raises a :exc:`KeyboardInterrupt` exception
+on Windows, similar to how those functions behave on macOS and Linux.
diff --git a/Python/parking_lot.c b/Python/parking_lot.c
index a7e9760e35d87a..bffc959e5d0978 100644
--- a/Python/parking_lot.c
+++ b/Python/parking_lot.c
@@ -111,15 +111,28 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t 
timeout)
 millis = (DWORD) div;
 }
 }
-wait = WaitForSingleObjectEx(sema->platform_sem, millis, FALSE);
+
+// NOTE: we wait on the sigint event even in non-main threads to match the
+// behavior of the other platforms. Non-main threads will ignore the
+// Py_PARK_INTR result.
+HANDLE sigint_event = _PyOS_SigintEvent();
+HANDLE handles[2] = { sema->platform_sem, sigint_event };
+DWORD count = sigint_event != NULL ? 2 : 1;
+wait = WaitForMultipleObjects(count, handles, FALSE, millis);
 if (wait == WAIT_OBJECT_0) {
 res = Py_PARK_OK;
 }
+else if (wait == WAIT_OBJECT_0 + 1) {
+ResetEvent(sigint_event);
+res = Py_PARK_INTR;
+}
 else if (wait == WAIT_TIMEOUT) {
 res = Py_PARK_TIMEOUT;
 }
 else {
-res = Py_PARK_INTR;
+_Py_FatalErrorFormat(__func__,
+"unexpected error from semaphore: %u (error: %u)",
+wait, GetLastError());
 }
 #elif defined(_Py_USE_SEMAPHORES)
 int err;

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-130115: fix thread identifiers for 32-bit musl (#130391)

2025-04-04 Thread pitrou
https://github.com/python/cpython/commit/72123063ddee84bb2c9d591a23f420997e35af5a
commit: 72123063ddee84bb2c9d591a23f420997e35af5a
branch: main
author: Vincent Fazio 
committer: pitrou 
date: 2025-04-04T16:31:37+02:00
summary:

gh-130115: fix thread identifiers for 32-bit musl (#130391)

CPython's pthread-based thread identifier relies on pthread_t being able
to be represented as an unsigned integer type.

This is true in most Linux libc implementations where it's defined as an
unsigned long, however musl typedefs it as a struct *.

If the pointer has the high bit set and is cast to PyThread_ident_t, the
resultant value can be sign-extended [0]. This can cause issues when
comparing against threading._MainThread's identifier. The main thread's
identifier value is retrieved via _get_main_thread_ident which is backed
by an unsigned long which truncates sign extended bits.

  >>> hex(threading.main_thread().ident)
  '0xb6f33f3c'
  >>> hex(threading.current_thread().ident)
  '0xb6f33f3c'

Work around this by conditionally compiling in some code for non-glibc
based Linux platforms that are at risk of sign-extension to return a
PyLong based on the main thread's unsigned long thread identifier if the
current thread is the main thread.

[0]: 
https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html

-

Signed-off-by: Vincent Fazio 

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
M Python/thread_pthread.h

diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
new file mode 100644
index 00..124da33f8836f6
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
@@ -0,0 +1 @@
+Fix an issue with thread identifiers being sign-extended on some platforms.
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index c010b3a827757f..da4058242448f3 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -306,6 +306,24 @@ do_start_joinable_thread(void (*func)(void *), void *arg, 
pthread_t* out_id)
 return 0;
 }
 
+/* Helper to convert pthread_t to PyThread_ident_t. POSIX allows pthread_t to 
be
+   non-arithmetic, e.g., musl typedefs it as a pointer. */
+static PyThread_ident_t
+_pthread_t_to_ident(pthread_t value) {
+// Cast through an integer type of the same size to avoid sign-extension.
+#if SIZEOF_PTHREAD_T == SIZEOF_VOID_P
+return (uintptr_t) value;
+#elif SIZEOF_PTHREAD_T == SIZEOF_LONG
+return (unsigned long) value;
+#elif SIZEOF_PTHREAD_T == SIZEOF_INT
+return (unsigned int) value;
+#elif SIZEOF_PTHREAD_T == SIZEOF_LONG_LONG
+return (unsigned long long) value;
+#else
+#error "Unsupported SIZEOF_PTHREAD_T value"
+#endif
+}
+
 int
 PyThread_start_joinable_thread(void (*func)(void *), void *arg,
PyThread_ident_t* ident, PyThread_handle_t* 
handle) {
@@ -313,9 +331,8 @@ PyThread_start_joinable_thread(void (*func)(void *), void 
*arg,
 if (do_start_joinable_thread(func, arg, &th)) {
 return -1;
 }
-*ident = (PyThread_ident_t) th;
+*ident = _pthread_t_to_ident(th);
 *handle = (PyThread_handle_t) th;
-assert(th == (pthread_t) *ident);
 assert(th == (pthread_t) *handle);
 return 0;
 }
@@ -328,11 +345,7 @@ PyThread_start_new_thread(void (*func)(void *), void *arg)
 return PYTHREAD_INVALID_THREAD_ID;
 }
 pthread_detach(th);
-#if SIZEOF_PTHREAD_T <= SIZEOF_LONG
-return (unsigned long) th;
-#else
-return (unsigned long) *(unsigned long *) &th;
-#endif
+return (unsigned long) _pthread_t_to_ident(th);;
 }
 
 int
@@ -357,8 +370,7 @@ PyThread_get_thread_ident_ex(void) {
 if (!initialized)
 PyThread_init_thread();
 threadid = pthread_self();
-assert(threadid == (pthread_t) (PyThread_ident_t) threadid);
-return (PyThread_ident_t) threadid;
+return _pthread_t_to_ident(threadid);
 }
 
 unsigned long

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] [3.13] gh-130115: fix thread identifiers for 32-bit musl (GH-130391) (GH-132089)

2025-04-04 Thread pitrou
https://github.com/python/cpython/commit/240c200ccef9ad31dea3977d70d3ab4177a1212d
commit: 240c200ccef9ad31dea3977d70d3ab4177a1212d
branch: 3.13
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: pitrou 
date: 2025-04-04T22:57:35+02:00
summary:

[3.13] gh-130115: fix thread identifiers for 32-bit musl (GH-130391) (GH-132089)

CPython's pthread-based thread identifier relies on pthread_t being able
to be represented as an unsigned integer type.

This is true in most Linux libc implementations where it's defined as an
unsigned long, however musl typedefs it as a struct *.

If the pointer has the high bit set and is cast to PyThread_ident_t, the
resultant value can be sign-extended [0]. This can cause issues when
comparing against threading._MainThread's identifier. The main thread's
identifier value is retrieved via _get_main_thread_ident which is backed
by an unsigned long which truncates sign extended bits.

  >>> hex(threading.main_thread().ident)
  '0xb6f33f3c'
  >>> hex(threading.current_thread().ident)
  '0xb6f33f3c'

Work around this by conditionally compiling in some code for non-glibc
based Linux platforms that are at risk of sign-extension to return a
PyLong based on the main thread's unsigned long thread identifier if the
current thread is the main thread.

[0]: 
https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html

-
(cherry picked from commit 72123063ddee84bb2c9d591a23f420997e35af5a)

Signed-off-by: Vincent Fazio 
Co-authored-by: Vincent Fazio 

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
M Python/thread_pthread.h

diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
new file mode 100644
index 00..124da33f8836f6
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-00-12-24.gh-issue-130115.mF-rP6.rst
@@ -0,0 +1 @@
+Fix an issue with thread identifiers being sign-extended on some platforms.
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index f588b4620da0d3..de95e733ce09f3 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -307,6 +307,24 @@ do_start_joinable_thread(void (*func)(void *), void *arg, 
pthread_t* out_id)
 return 0;
 }
 
+/* Helper to convert pthread_t to PyThread_ident_t. POSIX allows pthread_t to 
be
+   non-arithmetic, e.g., musl typedefs it as a pointer. */
+static PyThread_ident_t
+_pthread_t_to_ident(pthread_t value) {
+// Cast through an integer type of the same size to avoid sign-extension.
+#if SIZEOF_PTHREAD_T == SIZEOF_VOID_P
+return (uintptr_t) value;
+#elif SIZEOF_PTHREAD_T == SIZEOF_LONG
+return (unsigned long) value;
+#elif SIZEOF_PTHREAD_T == SIZEOF_INT
+return (unsigned int) value;
+#elif SIZEOF_PTHREAD_T == SIZEOF_LONG_LONG
+return (unsigned long long) value;
+#else
+#error "Unsupported SIZEOF_PTHREAD_T value"
+#endif
+}
+
 int
 PyThread_start_joinable_thread(void (*func)(void *), void *arg,
PyThread_ident_t* ident, PyThread_handle_t* 
handle) {
@@ -314,9 +332,8 @@ PyThread_start_joinable_thread(void (*func)(void *), void 
*arg,
 if (do_start_joinable_thread(func, arg, &th)) {
 return -1;
 }
-*ident = (PyThread_ident_t) th;
+*ident = _pthread_t_to_ident(th);
 *handle = (PyThread_handle_t) th;
-assert(th == (pthread_t) *ident);
 assert(th == (pthread_t) *handle);
 return 0;
 }
@@ -329,11 +346,7 @@ PyThread_start_new_thread(void (*func)(void *), void *arg)
 return PYTHREAD_INVALID_THREAD_ID;
 }
 pthread_detach(th);
-#if SIZEOF_PTHREAD_T <= SIZEOF_LONG
-return (unsigned long) th;
-#else
-return (unsigned long) *(unsigned long *) &th;
-#endif
+return (unsigned long) _pthread_t_to_ident(th);;
 }
 
 int
@@ -358,8 +371,7 @@ PyThread_get_thread_ident_ex(void) {
 if (!initialized)
 PyThread_init_thread();
 threadid = pthread_self();
-assert(threadid == (pthread_t) (PyThread_ident_t) threadid);
-return (PyThread_ident_t) threadid;
+return _pthread_t_to_ident(threadid);
 }
 
 unsigned long

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com