PEP 539: Update for draft implementation feedback

* Don't use `bool` types in the implementation, per discussion at https://github.com/python/cpython/pull/1362#discussion_r136357583
* Update for removal of --without-threads
* miscellaneous other clarifications and updates
This commit is contained in:
Erik Bray 2017-09-08 00:12:06 +02:00 committed by Nick Coghlan
parent e9cf15666f
commit 86ca2506f3
1 changed files with 73 additions and 70 deletions

View File

@ -16,15 +16,15 @@ Abstract
The proposal is to add a new Thread Local Storage (TLS) API to CPython which The proposal is to add a new Thread Local Storage (TLS) API to CPython which
would supersede use of the existing TLS API within the CPython interpreter, would supersede use of the existing TLS API within the CPython interpreter,
while deprecating the existing API. The new API is named "Thread Specific while deprecating the existing API. The new API is named the "Thread
Storage (TSS) API" (see `Rationale for Proposed Solution`_ for the origin of Specific Storage (TSS) API" (see `Rationale for Proposed Solution`_ for the
the name). origin of the name).
Because the existing TLS API is only used internally (it is not mentioned in Because the existing TLS API is only used internally (it is not mentioned in
the documentation, and the header that defines it, ``pythread.h``, is not the documentation, and the header that defines it, ``pythread.h``, is not
included in ``Python.h`` either directly or indirectly), this proposal probably included in ``Python.h`` either directly or indirectly), this proposal
only affects CPython, but might also affect other interpreter implementations probably only affects CPython, but might also affect other interpreter
(PyPy?) that implement parts of the CPython API. implementations (PyPy?) that implement parts of the CPython API.
This is motivated primarily by the fact that the old API uses ``int`` to This is motivated primarily by the fact that the old API uses ``int`` to
represent TLS keys across all platforms, which is neither POSIX-compliant, represent TLS keys across all platforms, which is neither POSIX-compliant,
@ -63,7 +63,7 @@ The specification also adds a few new features:
depend on the underlying TLS implementation. It is defined:: depend on the underlying TLS implementation. It is defined::
typedef struct { typedef struct {
bool _is_initialized; int _is_initialized;
NATIVE_TSS_KEY_T _key; NATIVE_TSS_KEY_T _key;
} Py_tss_t; } Py_tss_t;
@ -77,25 +77,26 @@ The specification also adds a few new features:
PyAPI_FUNC(Py_tss_t *) PyThread_tss_alloc(void) PyAPI_FUNC(Py_tss_t *) PyThread_tss_alloc(void)
PyAPI_FUNC(void) PyThread_tss_free(Py_tss_t *key) PyAPI_FUNC(void) PyThread_tss_free(Py_tss_t *key)
PyAPI_FUNC(bool) PyThread_tss_is_created(Py_tss_t *key) PyAPI_FUNC(int) PyThread_tss_is_created(Py_tss_t *key)
The first two are needed for dynamic (de-)allocation of a ``Py_tss_t``, The first two are needed for dynamic (de-)allocation of a ``Py_tss_t``,
particularly in extension modules built with ``Py_LIMITED_API``, where particularly in extension modules built with ``Py_LIMITED_API``, where
static allocation of this type is not possible due to its implementation static allocation of this type is not possible due to its implementation
being opaque at build time. A value returned by ``PyThread_tss_alloc`` being opaque at build time. A value returned by ``PyThread_tss_alloc`` is
is the same state as initialized by ``Py_tss_NEEDS_INIT``, or ``NULL`` for in the same state as a value initialized with ``Py_tss_NEEDS_INIT``, or
dynamic allocation failure. The behavior of ``PyThread_tss_free`` involves ``NULL`` in the case of dynamic allocation failure. The behavior of
calling ``PyThread_tss_delete`` preventively, or is no-op if the value ``PyThread_tss_free`` involves calling ``PyThread_tss_delete``
pointed to by the ``key`` argument is ``NULL``. ``PyThread_tss_is_created`` preventively, or is a no-op if the value pointed to by the ``key``
returns ``true`` if the given ``Py_tss_t`` has been initialized (i.e. by argument is ``NULL``. ``PyThread_tss_is_created`` returns non-zero if the
``PyThread_tss_create``). given ``Py_tss_t`` has been initialized (i.e. by ``PyThread_tss_create``).
The new TSS API does not provide functions which correspond to The new TSS API does not provide functions which correspond to
``PyThread_delete_key_value`` and ``PyThread_ReInitTLS``, because these ``PyThread_delete_key_value`` and ``PyThread_ReInitTLS``, because these
functions are for the removed CPython's own TLS implementation, that is the functions were needed only for CPython's now defunct built-in TLS
existing API behavior has become as follows: ``PyThread_delete_key_value(key)`` implementation; that is the existing behavior of these functions is treated
is equal to ``PyThread_set_key_value(key, NULL)``, and ``PyThread_ReInitTLS()`` as follows: ``PyThread_delete_key_value(key)`` is equalivalent to
is no-op [8]_. ``PyThread_set_key_value(key, NULL)``, and ``PyThread_ReInitTLS()`` is a
no-op [8]_.
The new ``PyThread_tss_`` functions are almost exactly analogous to their The new ``PyThread_tss_`` functions are almost exactly analogous to their
original counterparts with a few minor differences: Whereas original counterparts with a few minor differences: Whereas
@ -116,17 +117,17 @@ any size. This is especially necessary for extension modules built with
value pointed to by the ``key`` argument is ``NULL``. value pointed to by the ``key`` argument is ``NULL``.
Moreover, because of the use of ``Py_tss_t`` instead of ``int``, there are Moreover, because of the use of ``Py_tss_t`` instead of ``int``, there are
additional behaviors which the existing API design would be carried over into behaviors in the new API which differ from the existing API with regard to
new API: The TSS key creation and deletion are parts of "do-if-needed" flow key creation and deletion. ``PyThread_tss_create`` can be called repeatedly
and these features are silently skipped if already done--Calling on the same key--calling it on an already initialized key is a no-op and
``PyThread_tss_create`` with an initialized key does nothing and returns immediately returns success. Similarly for calling ``PyThread_tss_delete``
success soon. This is also the case of calling ``PyThread_tss_delete`` with with an uninitialized key.
an uninitialized key.
The behavior of ``PyThread_tss_delete`` is defined to change the key's The behavior of ``PyThread_tss_delete`` is defined to change the key's
initialization state to "uninitialized" in order to restart the CPython initialization state to "uninitialized"--this allows, for example,
interpreter without terminating the process (e.g. embedding Python in an statically allocated keys to be reset to a sensible state when restarting
application) [12]_. the CPython interpreter without terminating the process (e.g. embedding
Python in an application) [12]_.
The old ``PyThread_*_key*`` functions will be marked as deprecated in the The old ``PyThread_*_key*`` functions will be marked as deprecated in the
documentation, but will not generate runtime deprecation warnings. documentation, but will not generate runtime deprecation warnings.
@ -150,20 +151,20 @@ Features - create key - create key
- set value - set value - set value - set value
- get value - get value - get value - get value
- delete value - (set ``NULL`` instead) [8]_ - delete value - (set ``NULL`` instead) [8]_
- reinitialize keys (for - (unnecessary) [8]_ - reinitialize keys (after - (unnecessary) [8]_
after fork) fork)
- dynamically (de-)allocate - dynamically (de-)allocate
key key
- check key's initialization - check key's initialization
state state
Default Value (``-1`` as key creation ``Py_tss_NEEDS_INIT`` Default Value (``-1`` as key creation ``Py_tss_NEEDS_INIT``
failure) failure)
Requirement native thread native thread Requirement native threads native threads
(since CPython 3.7 [9]_) (since CPython 3.7 [9]_)
Restriction Not support platform where Unable to statically allocate Restriction No support for platforms Unable to statically allocate
native TLS key is defined in key when ``Py_LIMITED_API`` where native TLS key is keys when ``Py_LIMITED_API``
a way that cannot be safely is defined. defined in a way that cannot is defined.
cast to ``int``. be safely cast to ``int``.
================= ============================= ============================= ================= ============================= =============================
Example Example
@ -209,10 +210,10 @@ Platform Support Changes
A new "Native Thread Implementation" section will be added to PEP 11 that A new "Native Thread Implementation" section will be added to PEP 11 that
states: states:
* As of CPython 3.7, in the case of enabling thread support, all platforms are * As of CPython 3.7, all platforms are required to provide a native thread
required to provide at least one of native thread implementation (as of implementation (such as pthreads or Windows) to implement the TSS
pthreads or Windows) to implement TSS API. Any TSS API problems that occur API. Any TSS API problems that occur in an implementation without native
in the implementation without native thread will be closed as "won't fix". threads will be closed as "won't fix".
Motivation Motivation
@ -223,11 +224,11 @@ TLS values, as defined by the original PyThread TLS API.
The original TLS API was added to Python by GvR back in 1997, and at the The original TLS API was added to Python by GvR back in 1997, and at the
time the key used to represent a TLS value was an ``int``, and so it has time the key used to represent a TLS value was an ``int``, and so it has
been to the time of writing. This used CPython's own TLS implementation, been to the time of writing. This used CPython's own TLS implementation
but the current generation of which hasn't been used, largely unchanged, in which long remained unused, largely unchanged, in Python/thread.c. Support
Python/thread.c. Support for implementation of the API on top of native for implementation of the API on top of native thread implementations
thread implementations (pthreads and Windows) was added much later, and the (pthreads and Windows) was added much later, and the built-in implementation
own implementation has been no longer necessary and removed [9]_. has been deemed no longer necessary and has since been removed [9]_.
The problem with the choice of ``int`` to represent a TLS key, is that while The problem with the choice of ``int`` to represent a TLS key, is that while
it was fine for CPython's own TLS implementation, and happens to be it was fine for CPython's own TLS implementation, and happens to be
@ -265,16 +266,17 @@ Rationale for Proposed Solution
=============================== ===============================
The use of an opaque type (``Py_tss_t``) to key TLS values allows the API to The use of an opaque type (``Py_tss_t``) to key TLS values allows the API to
be compatible, with all present (POSIX and Windows) and future (C11?) be compatible with all present (POSIX and Windows) and future (C11?) native
native TLS implementations supported by CPython, as it allows the definition TLS implementations supported by CPython, as it allows the definition of
of ``Py_tss_t`` to depend on the underlying implementation. ``Py_tss_t`` to depend on the underlying implementation.
Since the existing TLS API has been available in *the limited API* [13]_ for Since the existing TLS API has been available in *the limited API* [13]_ for
some platforms (e.g. Linux), CPython makes an effort to provide the new TSS API some platforms (e.g. Linux), CPython makes an effort to provide the new TSS
at that level likewise. Note, however, that ``Py_tss_t`` definition becomes to API at that level likewise. Note, however, that the ``Py_tss_t`` definition
be an opaque struct when ``Py_LIMITED_API`` is defined, because exposing becomes to be an opaque struct when ``Py_LIMITED_API`` is defined, because
``NATIVE_TSS_KEY_T`` as part of the limited API would prevent us from switching exposing ``NATIVE_TSS_KEY_T`` as part of the limited API would prevent us
native thread implementation without rebuilding extension module. from switching native thread implementation without rebuilding extension
modules.
A new API must be introduced, rather than changing the function signatures of A new API must be introduced, rather than changing the function signatures of
the current API, in order to maintain backwards compatibility. The new API the current API, in order to maintain backwards compatibility. The new API
@ -316,29 +318,29 @@ Rejected Ideas
wishing to be supported by CPython should follow the requirements of wishing to be supported by CPython should follow the requirements of
PEP-11. As explained above, while this would be a fair argument if PEP-11. As explained above, while this would be a fair argument if
CPython were being to asked to make changes to support particular quirks CPython were being to asked to make changes to support particular quirks
or features of a specific platform, in this case it is quirk of CPython or features of a specific platform, in this case it is a quirk of CPython
that prevents it from being used to its full potential on otherwise that prevents it from being used to its full potential on otherwise
POSIX-compliant platforms. The fact that the current implementation POSIX-compliant platforms. The fact that the current implementation
happens to work on Linux is a happy accident, and there's no guarantee happens to work on Linux is a happy accident, and there's no guarantee
that this will never change. that this will never change.
* Affected platforms should just configure Python ``--without-threads``: * Affected platforms should just configure Python ``--without-threads``:
This is a possible temporary workaround to the issue, but only that. this is no longer an option as the ``--without-threads`` option has
Python should not be hobbled on affected platforms despite them being been removed for Python 3.7 [16]_.
otherwise perfectly capable of running multi-threaded Python.
* Affected platforms should use CPython's own TLS implementation instead of * Affected platforms should use CPython's built-in TLS implementation
native TLS implementation: This is a more acceptable alternative to the instead of a native TLS implementation: This is a more acceptable
previous idea, and in fact there had been a patch to do just that [4]_. alternative to the previous idea, and in fact there had been a patch to do
However, the own implementation being "slower and clunkier" in general just that [4]_. However, the built-in implementation being "slower and
than native implementations still needlessly hobbles performance on affected clunkier" in general than native implementations still needlessly hobbles
platforms. At least one other module (``tracemalloc``) is also broken if performance on affected platforms. At least one other module
Python is built without native implementation. And this idea cannot be (``tracemalloc``) is also broken if Python is built without a native TLS
adopted because the own implementation was removed. implementation. This idea also cannot be adopted because the built-in
implementation has since been removed.
* Keep the existing API, but work around the issue by providing a mapping from * Keep the existing API, but work around the issue by providing a mapping from
``pthread_key_t`` values to ``int`` values. A couple attempts were made at ``pthread_key_t`` values to ``int`` values. A couple attempts were made at
this ([5]_, [6]_), but this only injects needless complexity and overhead this ([5]_, [6]_), but this injects needless complexity and overhead
into performance-critical code on platforms that are not currently affected into performance-critical code on platforms that are not currently affected
by this issue (such as Linux). Even if use of this workaround were made by this issue (such as Linux). Even if use of this workaround were made
conditional on platform compatibility, it introduces platform-specific code conditional on platform compatibility, it introduces platform-specific code
@ -350,13 +352,13 @@ Implementation
============== ==============
An initial version of a patch [7]_ is available on the bug tracker for this An initial version of a patch [7]_ is available on the bug tracker for this
issue. Since the migration to Github, it's being developed in the issue. Since the migration to GitHub, its development has continued in the
``pep539-tss-api`` feature branch [10]_ in Masayuki Yamamoto's fork of the ``pep539-tss-api`` feature branch [10]_ in Masayuki Yamamoto's fork of the
CPython repository on Github. A work-in-progress PR is available at [11]_. CPython repository on GitHub. A work-in-progress PR is available at [11]_.
This reference implementation covers not only the enhancement request in API This reference implementation covers not only the new API implementation
features, but also the client codes fix needed to replace the existing TLS API features, but also the client code updates needed to replace the existing
with the new TSS API. TLS API with the new TSS API.
Copyright Copyright
@ -384,3 +386,4 @@ References and Footnotes
(https://www.python.org/dev/peps/pep-0384/) (https://www.python.org/dev/peps/pep-0384/)
.. [14] http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.html .. [14] http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.html
.. [15] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=404 .. [15] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=404
.. [16] https://bugs.python.org/issue31370