gh-110014: Fix _POSIX_THREADS and _POSIX_SEMAPHORES usage (#110139)

* pycore_pythread.h is now the central place to make sure that
  _POSIX_THREADS and _POSIX_SEMAPHORES macros are defined if
  available.
* Make sure that pycore_pythread.h is included when _POSIX_THREADS
  and _POSIX_SEMAPHORES macros are tested.
* PY_TIMEOUT_MAX is now defined as a constant, since its value
  depends on _POSIX_THREADS, instead of being defined as a macro.
* Prevent integer overflow in the preprocessor when computing
  PY_TIMEOUT_MAX_VALUE on Windows:
  replace "0xFFFFFFFELL * 1000 < LLONG_MAX"
  with "0xFFFFFFFELL < LLONG_MAX / 1000".
* Document the change and give hints how to fix affected code.
* Add an exception for PY_TIMEOUT_MAX  name to smelly.py
* Add PY_TIMEOUT_MAX to the stable ABI
This commit is contained in:
Victor Stinner 2023-09-30 19:25:54 +02:00 committed by GitHub
parent f3bb00ea12
commit 74e425ec18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 72 additions and 57 deletions

View File

@ -1,4 +1,5 @@
role,name,added,ifdef_note,struct_abi_kind role,name,added,ifdef_note,struct_abi_kind
var,PY_TIMEOUT_MAX,3.2,,
macro,PY_VECTORCALL_ARGUMENTS_OFFSET,3.12,, macro,PY_VECTORCALL_ARGUMENTS_OFFSET,3.12,,
function,PyAIter_Check,3.10,, function,PyAIter_Check,3.10,,
function,PyArg_Parse,3.2,, function,PyArg_Parse,3.2,,

View File

@ -989,6 +989,10 @@ Porting to Python 3.13
* ``Python.h`` no longer includes the ``<unistd.h>`` standard header file. If * ``Python.h`` no longer includes the ``<unistd.h>`` standard header file. If
needed, it should now be included explicitly. For example, it provides the needed, it should now be included explicitly. For example, it provides the
functions: ``close()``, ``getpagesize()``, ``getpid()`` and ``sysconf()``. functions: ``close()``, ``getpagesize()``, ``getpid()`` and ``sysconf()``.
As a consequence, ``_POSIX_SEMAPHORES`` and ``_POSIX_THREADS`` macros are no
longer defined by ``Python.h``. The ``HAVE_UNISTD_H`` and ``HAVE_PTHREAD_H``
macros defined by ``Python.h`` can be used to decide if ``<unistd.h>`` and
``<pthread.h>`` header files can be included.
(Contributed by Victor Stinner in :gh:`108765`.) (Contributed by Victor Stinner in :gh:`108765`.)
* ``Python.h`` no longer includes these standard header files: ``<time.h>``, * ``Python.h`` no longer includes these standard header files: ``<time.h>``,

View File

@ -5,18 +5,8 @@
# error "this header requires Py_BUILD_CORE define" # error "this header requires Py_BUILD_CORE define"
#endif #endif
#ifndef MS_WINDOWS #include "pycore_pythread.h" // _POSIX_THREADS
# include <unistd.h> // _POSIX_THREADS
#endif
#ifndef _POSIX_THREADS
/* This means pthreads are not implemented in libc headers, hence the macro
not present in unistd.h. But they still can be implemented as an external
library (e.g. gnu pth in pthread emulation) */
# ifdef HAVE_PTHREAD_H
# include <pthread.h> // _POSIX_THREADS
# endif
#endif
#ifdef _POSIX_THREADS #ifdef _POSIX_THREADS
/* /*

View File

@ -9,29 +9,29 @@ extern "C" {
#endif #endif
#ifndef _POSIX_THREADS // Get _POSIX_THREADS and _POSIX_SEMAPHORES macros if available
/* This means pthreads are not implemented in libc headers, hence the macro #if (defined(HAVE_UNISTD_H) && !defined(_POSIX_THREADS) \
not present in unistd.h. But they still can be implemented as an external && !defined(_POSIX_SEMAPHORES))
library (e.g. gnu pth in pthread emulation) */ # include <unistd.h> // _POSIX_THREADS, _POSIX_SEMAPHORES
# ifdef HAVE_PTHREAD_H
# include <pthread.h> // _POSIX_THREADS
#endif #endif
# ifndef _POSIX_THREADS #if (defined(HAVE_PTHREAD_H) && !defined(_POSIX_THREADS) \
/* Check if we're running on HP-UX and _SC_THREADS is defined. If so, then && !defined(_POSIX_SEMAPHORES))
enough of the Posix threads package is implemented to support python // This means pthreads are not implemented in libc headers, hence the macro
threads. // not present in <unistd.h>. But they still can be implemented as an
// external library (e.g. gnu pth in pthread emulation)
This is valid for HP-UX 11.23 running on an ia64 system. If needed, add # include <pthread.h> // _POSIX_THREADS, _POSIX_SEMAPHORES
a check of __ia64 to verify that we're running on an ia64 system instead #endif
of a pa-risc system. #if !defined(_POSIX_THREADS) && defined(__hpux) && defined(_SC_THREADS)
*/ // Check if we're running on HP-UX and _SC_THREADS is defined. If so, then
# ifdef __hpux // enough of the POSIX threads package is implemented to support Python
# ifdef _SC_THREADS // threads.
//
// This is valid for HP-UX 11.23 running on an ia64 system. If needed, add
// a check of __ia64 to verify that we're running on an ia64 system instead
// of a pa-risc system.
# define _POSIX_THREADS # define _POSIX_THREADS
#endif #endif
# endif
# endif /* _POSIX_THREADS */
#endif /* _POSIX_THREADS */
#if defined(_POSIX_THREADS) || defined(HAVE_PTHREAD_STUBS) #if defined(_POSIX_THREADS) || defined(HAVE_PTHREAD_STUBS)
# define _USE_PTHREADS # define _USE_PTHREADS

View File

@ -7,6 +7,7 @@
# error "this header requires Py_BUILD_CORE define" # error "this header requires Py_BUILD_CORE define"
#endif #endif
#include "pycore_pythread.h" // _POSIX_SEMAPHORES
#include "pycore_time.h" // _PyTime_t #include "pycore_time.h" // _PyTime_t
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -26,6 +27,7 @@
# include <semaphore.h> # include <semaphore.h>
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@ -44,22 +44,7 @@ PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int);
*/ */
#define PY_TIMEOUT_T long long #define PY_TIMEOUT_T long long
#if defined(_POSIX_THREADS) PyAPI_DATA(const long long) PY_TIMEOUT_MAX;
/* PyThread_acquire_lock_timed() uses _PyTime_FromNanoseconds(us * 1000),
convert microseconds to nanoseconds. */
# define PY_TIMEOUT_MAX (LLONG_MAX / 1000)
#elif defined (NT_THREADS)
// WaitForSingleObject() accepts timeout in milliseconds in the range
// [0; 0xFFFFFFFE] (DWORD type). INFINITE value (0xFFFFFFFF) means no
// timeout. 0xFFFFFFFE milliseconds is around 49.7 days.
# if 0xFFFFFFFELL * 1000 < LLONG_MAX
# define PY_TIMEOUT_MAX (0xFFFFFFFELL * 1000)
# else
# define PY_TIMEOUT_MAX LLONG_MAX
# endif
#else
# define PY_TIMEOUT_MAX LLONG_MAX
#endif
/* If microseconds == 0, the call is non-blocking: it returns immediately /* If microseconds == 0, the call is non-blocking: it returns immediately

View File

@ -35,6 +35,7 @@ class TestStableABIAvailability(unittest.TestCase):
SYMBOL_NAMES = ( SYMBOL_NAMES = (
"PY_TIMEOUT_MAX",
"PyAIter_Check", "PyAIter_Check",
"PyArg_Parse", "PyArg_Parse",
"PyArg_ParseTuple", "PyArg_ParseTuple",

View File

@ -1843,6 +1843,10 @@
[function.PyThread_start_new_thread] [function.PyThread_start_new_thread]
added = '3.2' added = '3.2'
# Not mentioned in PEP 384, was implemented as a macro in Python <= 3.12
[data.PY_TIMEOUT_MAX]
added = '3.2'
# The following were added in PC/python3.def in Python 3.3: # The following were added in PC/python3.def in Python 3.3:
# 7800f75827b1be557be16f3b18f5170fbf9fae08 # 7800f75827b1be557be16f3b18f5170fbf9fae08
# 9c56409d3353b8cd4cfc19e0467bbe23fd34fc92 # 9c56409d3353b8cd4cfc19e0467bbe23fd34fc92

1
PC/python3dll.c generated
View File

@ -768,6 +768,7 @@ EXPORT_DATA(Py_FileSystemDefaultEncodeErrors)
EXPORT_DATA(Py_FileSystemDefaultEncoding) EXPORT_DATA(Py_FileSystemDefaultEncoding)
EXPORT_DATA(Py_GenericAliasType) EXPORT_DATA(Py_GenericAliasType)
EXPORT_DATA(Py_HasFileSystemDefaultEncoding) EXPORT_DATA(Py_HasFileSystemDefaultEncoding)
EXPORT_DATA(PY_TIMEOUT_MAX)
EXPORT_DATA(Py_UTF8Mode) EXPORT_DATA(Py_UTF8Mode)
EXPORT_DATA(Py_Version) EXPORT_DATA(Py_Version)
EXPORT_DATA(PyBaseObject_Type) EXPORT_DATA(PyBaseObject_Type)

View File

@ -41,7 +41,8 @@
#define _CONDVAR_IMPL_H_ #define _CONDVAR_IMPL_H_
#include "Python.h" #include "Python.h"
#include "pycore_condvar.h" #include "pycore_pythread.h" // _POSIX_THREADS
#ifdef _POSIX_THREADS #ifdef _POSIX_THREADS
/* /*

View File

@ -8,7 +8,7 @@
#include "Python.h" #include "Python.h"
#include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_structseq.h" // _PyStructSequence_FiniBuiltin() #include "pycore_structseq.h" // _PyStructSequence_FiniBuiltin()
#include "pycore_pythread.h" #include "pycore_pythread.h" // _POSIX_THREADS
#ifndef DONT_HAVE_STDIO_H #ifndef DONT_HAVE_STDIO_H
# include <stdio.h> # include <stdio.h>
@ -17,6 +17,26 @@
#include <stdlib.h> #include <stdlib.h>
// Define PY_TIMEOUT_MAX constant.
#ifdef _POSIX_THREADS
// PyThread_acquire_lock_timed() uses _PyTime_FromNanoseconds(us * 1000),
// convert microseconds to nanoseconds.
# define PY_TIMEOUT_MAX_VALUE (LLONG_MAX / 1000)
#elif defined (NT_THREADS)
// WaitForSingleObject() accepts timeout in milliseconds in the range
// [0; 0xFFFFFFFE] (DWORD type). INFINITE value (0xFFFFFFFF) means no
// timeout. 0xFFFFFFFE milliseconds is around 49.7 days.
# if 0xFFFFFFFELL < LLONG_MAX / 1000
# define PY_TIMEOUT_MAX_VALUE (0xFFFFFFFELL * 1000)
# else
# define PY_TIMEOUT_MAX_VALUE LLONG_MAX
# endif
#else
# define PY_TIMEOUT_MAX_VALUE LLONG_MAX
#endif
const long long PY_TIMEOUT_MAX = PY_TIMEOUT_MAX_VALUE;
static void PyThread__init_thread(void); /* Forward */ static void PyThread__init_thread(void); /* Forward */
#define initialized _PyRuntime.threads.initialized #define initialized _PyRuntime.threads.initialized

View File

@ -1,4 +1,5 @@
#include "pycore_interp.h" // _PyInterpreterState.threads.stacksize #include "pycore_interp.h" // _PyInterpreterState.threads.stacksize
#include "pycore_pythread.h" // _POSIX_SEMAPHORES
/* Posix threads interface */ /* Posix threads interface */

View File

@ -11,6 +11,11 @@ ALLOWED_PREFIXES = ('Py', '_Py')
if sys.platform == 'darwin': if sys.platform == 'darwin':
ALLOWED_PREFIXES += ('__Py',) ALLOWED_PREFIXES += ('__Py',)
# "Legacy": some old symbols are prefixed by "PY_".
EXCEPTIONS = frozenset({
'PY_TIMEOUT_MAX',
})
IGNORED_EXTENSION = "_ctypes_test" IGNORED_EXTENSION = "_ctypes_test"
# Ignore constructor and destructor functions # Ignore constructor and destructor functions
IGNORED_SYMBOLS = {'_init', '_fini'} IGNORED_SYMBOLS = {'_init', '_fini'}
@ -72,7 +77,7 @@ def get_smelly_symbols(stdout):
symbol = parts[-1] symbol = parts[-1]
result = '%s (type: %s)' % (symbol, symtype) result = '%s (type: %s)' % (symbol, symtype)
if symbol.startswith(ALLOWED_PREFIXES): if symbol.startswith(ALLOWED_PREFIXES) or symbol in EXCEPTIONS:
python_symbols.append(result) python_symbols.append(result)
continue continue