PEP 539: update draft (#316)

This commit is contained in:
Masayuki Yamamoto 2017-08-29 06:35:54 +09:00 committed by Brett Cannon
parent 14e14c6dce
commit 7a99bb9b72
1 changed files with 92 additions and 17 deletions

View File

@ -57,10 +57,6 @@ These would be superseded by a new set of analogous functions::
PyAPI_FUNC(int) PyThread_tss_set(Py_tss_t *key, void *value)
PyAPI_FUNC(void *) PyThread_tss_get(Py_tss_t *key)
``PyThread_delete_key_value`` and ``PyThread_ReInitTLS`` don't provide
on the new TSS API because these functions are for the CPython's own TLS
implementation that was removed [8]_.
The specification also adds a few new features:
* A new type ``Py_tss_t``--an opaque type the definition of which may
@ -83,16 +79,26 @@ The specification also adds a few new features:
PyAPI_FUNC(void) PyThread_tss_free(Py_tss_t *key)
PyAPI_FUNC(bool) PyThread_tss_is_created(Py_tss_t *key)
The first two are needed for dynamic (de-)allocation of a `Py_tss_t`,
particularly in extension modules built with `Py_LIMITED_API`, where
The first two are needed for dynamic (de-)allocation of a ``Py_tss_t``,
particularly in extension modules built with ``Py_LIMITED_API``, where
static allocation of this type is not possible due to its implementation
being opaque at build time. The value returned by ``PyThread_tss_alloc``
is the same state (not yet initialized key) as statically allocated with
``Py_tss_NEEDS_INIT``. ``PyThread_tss_is_created`` returns ``true`` if the
given ``Py_tss_t`` has been initialized (i.e. by ``PyThread_tss_create``).
being opaque at build time. A value returned by ``PyThread_tss_alloc``
is the same state as initialized by ``Py_tss_NEEDS_INIT``, or ``NULL`` for
dynamic allocation failure. The behavior of ``PyThread_tss_free`` involves
calling ``PyThread_tss_delete`` preventively, or is no-op if the value
pointed to by the ``key`` argument is ``NULL``. ``PyThread_tss_is_created``
returns ``true`` if the given ``Py_tss_t`` has been initialized (i.e. by
``PyThread_tss_create``).
The new TSS API does not provide functions which correspond to
``PyThread_delete_key_value`` and ``PyThread_ReInitTLS``, because these
functions are for the removed CPython's own TLS implementation, that is the
existing API behavior has become as follows: ``PyThread_delete_key_value(key)``
is equal to ``PyThread_set_key_value(key, NULL)``, and ``PyThread_ReInitTLS()``
is no-op [8]_.
The new ``PyThread_tss_`` functions are almost exactly analogous to their
original counterparts with a minor difference: Whereas
original counterparts with a few minor differences: Whereas
``PyThread_create_key`` takes no arguments and returns a TLS key as an
``int``, ``PyThread_tss_create`` takes a ``Py_tss_t*`` as an argument and
returns an ``int`` status code. The behavior of ``PyThread_tss_create`` is
@ -105,7 +111,22 @@ Similarly the other ``PyThread_tss_`` functions are passed a ``Py_tss_t*``
whereas previously the key was passed by value. This change is necessary, as
being an opaque type, the ``Py_tss_t`` type could hypothetically be almost
any size. This is especially necessary for extension modules built with
``Py_LIMITED_API``, where the size of the type is not known.
``Py_LIMITED_API``, where the size of the type is not known. Except for
``PyThread_tss_free``, the behaviors of ``PyThread_tss_`` are undefined if the
value pointed to by the ``key`` argument is ``NULL``.
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
new API: The TSS key creation and deletion are parts of "do-if-needed" flow
and these features are silently skipped if already done--Calling
``PyThread_tss_create`` with an initialized key does nothing and returns
success soon. This is also the case of calling ``PyThread_tss_delete`` with
an uninitialized key.
The behavior of ``PyThread_tss_delete`` is defined to change the key's
initialization state to "uninitialized" in order to restart 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
documentation, but will not generate runtime deprecation warnings.
@ -114,6 +135,37 @@ Additionally, on platforms where ``sizeof(pthread_key_t) != sizeof(int)``,
``PyThread_create_key`` will return immediately with a failure status, and
the other TLS functions will all be no-ops on such platforms.
Comparison of API Specification
-------------------------------
================= ============================= =============================
API Thread Local Storage (TLS) Thread Specific Storage (TSS)
================= ============================= =============================
Version Existing New
Key Type ``int`` ``Py_tss_t`` (opaque type)
Handle Native Key cast to ``int`` conceal into internal field
Function Argument ``int`` ``Py_tss_t *``
Features - create key - create key
- delete key - delete key
- set value - set value
- get value - get value
- delete value - (set ``NULL`` instead) [8]_
- reinitialize keys (for - (unnecessary) [8]_
after fork)
- dynamically (de-)allocate
key
- check key's initialization
state
Default Value (``-1`` as key creation ``Py_tss_NEEDS_INIT``
failure)
Requirement native thread native thread
(since CPython 3.7 [9]_)
Restriction Not support platform where Unable to statically allocate
native TLS key is defined in key when ``Py_LIMITED_API``
a way that cannot be safely is defined.
cast to ``int``.
================= ============================= =============================
Example
-------
@ -139,11 +191,22 @@ The rest of the API is used analogously to the old API::
PyThread_tss_delete(&tss_key);
assert(!PyThread_tss_is_created(&tss_key));
When ``Py_LIMITED_API`` is defined, a TSS key must be dynamically allocated::
static Py_tss_t *ptr_key = PyThread_tss_alloc();
if (ptr_key == NULL) {
/* ... handle key allocation failure ... */
}
assert(!PyThread_tss_is_created(ptr_key));
/* ... once done with the key ... */
PyThread_tss_free(ptr_key);
ptr_key = NULL;
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:
* As of CPython 3.7, in the case of enabling thread support, all platforms are
@ -171,7 +234,7 @@ it was fine for CPython's own TLS implementation, and happens to be
compatible with Windows (which uses ``DWORD`` for the analogous data), it is
not compatible with the POSIX standard for the pthreads API, which defines
``pthread_key_t`` as an opaque type not further defined by the standard (as
with ``Py_tss_t`` described above). This leaves it up to the underlying
with ``Py_tss_t`` described above) [14]_. This leaves it up to the underlying
implementation how a ``pthread_key_t`` value is used to look up
thread-specific data.
@ -202,16 +265,23 @@ Rationale for Proposed Solution
===============================
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 TLS implementations supported by CPython, as it allows the definition
of ``Py_tss_t`` to depend on the underlying implementation.
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
at that level likewise. Note, however, that ``Py_tss_t`` definition becomes to
be an opaque struct when ``Py_LIMITED_API`` is defined, because exposing
``NATIVE_TSS_KEY_T`` as part of the limited API would prevent us from switching
native thread implementation without rebuilding extension module.
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
also more clearly groups together these related functions under a single name
prefix, ``PyThread_tss_``. The "tss" in the name stands for "thread-specific
storage", and was influenced by the naming and design of the "tss" API that is
part of the C11 threads API. However, this is in no way meant to imply
part of the C11 threads API [15]_. However, this is in no way meant to imply
compatibility with or support for the C11 threads API, or signal any future
intention of supporting C11--it's just the influence for the naming and design.
@ -305,7 +375,12 @@ References and Footnotes
.. [5] http://bugs.python.org/file44269/issue25658-1.patch
.. [6] http://bugs.python.org/file44303/key-constant-time.diff
.. [7] http://bugs.python.org/file46379/pythread-tss-3.patch
.. [8] http://bugs.python.org/issue25658#msg298342
.. [8] https://bugs.python.org/msg298342
.. [9] http://bugs.python.org/issue30832
.. [10] https://github.com/python/cpython/compare/master...ma8ma:pep539-tss-api
.. [11] https://github.com/python/cpython/pull/1362
.. [12] https://docs.python.org/3/c-api/init.html#c.Py_FinalizeEx
.. [13] It is also called as "stable ABI"
(https://www.python.org/dev/peps/pep-0384/)
.. [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