mirror of https://github.com/python/cpython
bpo-29102: Add a unique ID to PyInterpreterState. (#1639)
This commit is contained in:
parent
93fc20b73e
commit
e377416c10
|
@ -821,6 +821,14 @@ been created.
|
||||||
:c:func:`PyThreadState_Clear`.
|
:c:func:`PyThreadState_Clear`.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: PY_INT64_T PyInterpreterState_GetID(PyInterpreterState *interp)
|
||||||
|
|
||||||
|
Return the interpreter's unique ID. If there was any error in doing
|
||||||
|
so then -1 is returned and an error is set.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: PyObject* PyThreadState_GetDict()
|
.. c:function:: PyObject* PyThreadState_GetDict()
|
||||||
|
|
||||||
Return a dictionary in which extensions can store thread-specific state
|
Return a dictionary in which extensions can store thread-specific state
|
||||||
|
|
|
@ -28,6 +28,8 @@ typedef struct _is {
|
||||||
struct _is *next;
|
struct _is *next;
|
||||||
struct _ts *tstate_head;
|
struct _ts *tstate_head;
|
||||||
|
|
||||||
|
int64_t id;
|
||||||
|
|
||||||
PyObject *modules;
|
PyObject *modules;
|
||||||
PyObject *modules_by_index;
|
PyObject *modules_by_index;
|
||||||
PyObject *sysdict;
|
PyObject *sysdict;
|
||||||
|
@ -154,9 +156,16 @@ typedef struct _ts {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef Py_LIMITED_API
|
||||||
|
PyAPI_FUNC(void) _PyInterpreterState_Init(void);
|
||||||
|
#endif /* !Py_LIMITED_API */
|
||||||
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void);
|
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void);
|
||||||
PyAPI_FUNC(void) PyInterpreterState_Clear(PyInterpreterState *);
|
PyAPI_FUNC(void) PyInterpreterState_Clear(PyInterpreterState *);
|
||||||
PyAPI_FUNC(void) PyInterpreterState_Delete(PyInterpreterState *);
|
PyAPI_FUNC(void) PyInterpreterState_Delete(PyInterpreterState *);
|
||||||
|
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000
|
||||||
|
/* New in 3.7 */
|
||||||
|
PyAPI_FUNC(int64_t) PyInterpreterState_GetID(PyInterpreterState *);
|
||||||
|
#endif
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
PyAPI_FUNC(int) _PyState_AddModule(PyObject*, struct PyModuleDef*);
|
PyAPI_FUNC(int) _PyState_AddModule(PyObject*, struct PyModuleDef*);
|
||||||
#endif /* !Py_LIMITED_API */
|
#endif /* !Py_LIMITED_API */
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
# Run the _testcapi module tests (tests for the Python/C API): by defn,
|
# Run the _testcapi module tests (tests for the Python/C API): by defn,
|
||||||
# these are all functions _testcapi exports whose name begins with 'test_'.
|
# these are all functions _testcapi exports whose name begins with 'test_'.
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
|
import platform
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -384,12 +386,91 @@ class EmbeddingTests(unittest.TestCase):
|
||||||
return out, err
|
return out, err
|
||||||
|
|
||||||
def test_subinterps(self):
|
def test_subinterps(self):
|
||||||
# This is just a "don't crash" test
|
|
||||||
out, err = self.run_embedded_interpreter("repeated_init_and_subinterpreters")
|
out, err = self.run_embedded_interpreter("repeated_init_and_subinterpreters")
|
||||||
if support.verbose:
|
self.assertEqual(err, "")
|
||||||
print()
|
|
||||||
print(out)
|
# The output from _testembed looks like this:
|
||||||
print(err)
|
# --- Pass 0 ---
|
||||||
|
# interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728
|
||||||
|
# interp 1 <0x1d4f690>, thread state <0x1d35350>: id(modules) = 139650431165784
|
||||||
|
# interp 2 <0x1d5a690>, thread state <0x1d99ed0>: id(modules) = 139650413140368
|
||||||
|
# interp 3 <0x1d4f690>, thread state <0x1dc3340>: id(modules) = 139650412862200
|
||||||
|
# interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728
|
||||||
|
# --- Pass 1 ---
|
||||||
|
# ...
|
||||||
|
|
||||||
|
interp_pat = (r"^interp (\d+) <(0x[\dA-F]+)>, "
|
||||||
|
r"thread state <(0x[\dA-F]+)>: "
|
||||||
|
r"id\(modules\) = ([\d]+)$")
|
||||||
|
Interp = namedtuple("Interp", "id interp tstate modules")
|
||||||
|
|
||||||
|
main = None
|
||||||
|
lastmain = None
|
||||||
|
numinner = None
|
||||||
|
numloops = 0
|
||||||
|
for line in out.splitlines():
|
||||||
|
if line == "--- Pass {} ---".format(numloops):
|
||||||
|
if numinner is not None:
|
||||||
|
self.assertEqual(numinner, 5)
|
||||||
|
if support.verbose:
|
||||||
|
print(line)
|
||||||
|
lastmain = main
|
||||||
|
main = None
|
||||||
|
mainid = 0
|
||||||
|
numloops += 1
|
||||||
|
numinner = 0
|
||||||
|
continue
|
||||||
|
numinner += 1
|
||||||
|
|
||||||
|
self.assertLessEqual(numinner, 5)
|
||||||
|
match = re.match(interp_pat, line)
|
||||||
|
if match is None:
|
||||||
|
self.assertRegex(line, interp_pat)
|
||||||
|
|
||||||
|
# The last line in the loop should be the same as the first.
|
||||||
|
if numinner == 5:
|
||||||
|
self.assertEqual(match.groups(), main)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Parse the line from the loop. The first line is the main
|
||||||
|
# interpreter and the 3 afterward are subinterpreters.
|
||||||
|
interp = Interp(*match.groups())
|
||||||
|
if support.verbose:
|
||||||
|
print(interp)
|
||||||
|
if numinner == 1:
|
||||||
|
main = interp
|
||||||
|
id = str(mainid)
|
||||||
|
else:
|
||||||
|
subid = mainid + numinner - 1
|
||||||
|
id = str(subid)
|
||||||
|
|
||||||
|
# Validate the loop line for each interpreter.
|
||||||
|
self.assertEqual(interp.id, id)
|
||||||
|
self.assertTrue(interp.interp)
|
||||||
|
self.assertTrue(interp.tstate)
|
||||||
|
self.assertTrue(interp.modules)
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
# XXX Fix on Windows: something is going on with the
|
||||||
|
# pointers in Programs/_testembed.c. interp.interp
|
||||||
|
# is 0x0 and # interp.modules is the same between
|
||||||
|
# interpreters.
|
||||||
|
continue
|
||||||
|
if interp is main:
|
||||||
|
if lastmain is not None:
|
||||||
|
# A new main interpreter may have the same interp
|
||||||
|
# and/or tstate pointer as an earlier finalized/
|
||||||
|
# destroyed one. So we do not check interp or
|
||||||
|
# tstate here.
|
||||||
|
self.assertNotEqual(interp.modules, lastmain.modules)
|
||||||
|
else:
|
||||||
|
# A new subinterpreter may have the same
|
||||||
|
# PyInterpreterState pointer as a previous one if
|
||||||
|
# the earlier one has already been destroyed. So
|
||||||
|
# we compare with the main interpreter. The same
|
||||||
|
# applies to tstate.
|
||||||
|
self.assertNotEqual(interp.interp, main.interp)
|
||||||
|
self.assertNotEqual(interp.tstate, main.tstate)
|
||||||
|
self.assertNotEqual(interp.modules, main.modules)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_default_pipe_encoding():
|
def _get_default_pipe_encoding():
|
||||||
|
|
|
@ -53,6 +53,9 @@ Core and Builtins
|
||||||
- bpo-24821: Fixed the slowing down to 25 times in the searching of some
|
- bpo-24821: Fixed the slowing down to 25 times in the searching of some
|
||||||
unlucky Unicode characters.
|
unlucky Unicode characters.
|
||||||
|
|
||||||
|
- bpo-29102: Add a unique ID to PyInterpreterState. This makes it easier
|
||||||
|
to identify each subinterpreter.
|
||||||
|
|
||||||
- bpo-29894: The deprecation warning is emitted if __complex__ returns an
|
- bpo-29894: The deprecation warning is emitted if __complex__ returns an
|
||||||
instance of a strict subclass of complex. In a future versions of Python
|
instance of a strict subclass of complex. In a future versions of Python
|
||||||
this can be an error.
|
this can be an error.
|
||||||
|
|
|
@ -538,6 +538,7 @@ EXPORTS
|
||||||
PySlice_Type=python37.PySlice_Type DATA
|
PySlice_Type=python37.PySlice_Type DATA
|
||||||
PySlice_Unpack=python37.PySlice_Unpack
|
PySlice_Unpack=python37.PySlice_Unpack
|
||||||
PySortWrapper_Type=python37.PySortWrapper_Type DATA
|
PySortWrapper_Type=python37.PySortWrapper_Type DATA
|
||||||
|
PyInterpreterState_GetID=python37.PyInterpreterState_GetID
|
||||||
PyState_AddModule=python37.PyState_AddModule
|
PyState_AddModule=python37.PyState_AddModule
|
||||||
PyState_FindModule=python37.PyState_FindModule
|
PyState_FindModule=python37.PyState_FindModule
|
||||||
PyState_RemoveModule=python37.PyState_RemoveModule
|
PyState_RemoveModule=python37.PyState_RemoveModule
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
/*********************************************************
|
/*********************************************************
|
||||||
|
@ -22,9 +23,13 @@ static void _testembed_Py_Initialize(void)
|
||||||
|
|
||||||
static void print_subinterp(void)
|
static void print_subinterp(void)
|
||||||
{
|
{
|
||||||
/* Just output some debug stuff */
|
/* Output information about the interpreter in the format
|
||||||
|
expected in Lib/test/test_capi.py (test_subinterps). */
|
||||||
PyThreadState *ts = PyThreadState_Get();
|
PyThreadState *ts = PyThreadState_Get();
|
||||||
printf("interp %p, thread state %p: ", ts->interp, ts);
|
PyInterpreterState *interp = ts->interp;
|
||||||
|
int64_t id = PyInterpreterState_GetID(interp);
|
||||||
|
printf("interp %lu <0x%" PRIXPTR ">, thread state <0x%" PRIXPTR ">: ",
|
||||||
|
id, (uintptr_t)interp, (uintptr_t)ts);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
PyRun_SimpleString(
|
PyRun_SimpleString(
|
||||||
"import sys;"
|
"import sys;"
|
||||||
|
|
|
@ -344,6 +344,7 @@ _Py_InitializeEx_Private(int install_sigs, int install_importlib)
|
||||||
|
|
||||||
_PyRandom_Init();
|
_PyRandom_Init();
|
||||||
|
|
||||||
|
_PyInterpreterState_Init();
|
||||||
interp = PyInterpreterState_New();
|
interp = PyInterpreterState_New();
|
||||||
if (interp == NULL)
|
if (interp == NULL)
|
||||||
Py_FatalError("Py_Initialize: can't make first interpreter");
|
Py_FatalError("Py_Initialize: can't make first interpreter");
|
||||||
|
|
|
@ -65,6 +65,23 @@ PyThreadFrameGetter _PyThreadState_GetFrame = NULL;
|
||||||
static void _PyGILState_NoteThreadState(PyThreadState* tstate);
|
static void _PyGILState_NoteThreadState(PyThreadState* tstate);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* _next_interp_id is an auto-numbered sequence of small integers.
|
||||||
|
It gets initialized in _PyInterpreterState_Init(), which is called
|
||||||
|
in Py_Initialize(), and used in PyInterpreterState_New(). A negative
|
||||||
|
interpreter ID indicates an error occurred. The main interpreter
|
||||||
|
will always have an ID of 0. Overflow results in a RuntimeError.
|
||||||
|
If that becomes a problem later then we can adjust, e.g. by using
|
||||||
|
a Python int.
|
||||||
|
|
||||||
|
We initialize this to -1 so that the pre-Py_Initialize() value
|
||||||
|
results in an error. */
|
||||||
|
static int64_t _next_interp_id = -1;
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyInterpreterState_Init(void)
|
||||||
|
{
|
||||||
|
_next_interp_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
PyInterpreterState *
|
PyInterpreterState *
|
||||||
PyInterpreterState_New(void)
|
PyInterpreterState_New(void)
|
||||||
|
@ -103,6 +120,15 @@ PyInterpreterState_New(void)
|
||||||
HEAD_LOCK();
|
HEAD_LOCK();
|
||||||
interp->next = interp_head;
|
interp->next = interp_head;
|
||||||
interp_head = interp;
|
interp_head = interp;
|
||||||
|
if (_next_interp_id < 0) {
|
||||||
|
/* overflow or Py_Initialize() not called! */
|
||||||
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
|
"failed to get an interpreter ID");
|
||||||
|
interp = NULL;
|
||||||
|
} else {
|
||||||
|
interp->id = _next_interp_id;
|
||||||
|
_next_interp_id += 1;
|
||||||
|
}
|
||||||
HEAD_UNLOCK();
|
HEAD_UNLOCK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +196,17 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int64_t
|
||||||
|
PyInterpreterState_GetID(PyInterpreterState *interp)
|
||||||
|
{
|
||||||
|
if (interp == NULL) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "no interpreter provided");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return interp->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Default implementation for _PyThreadState_GetFrame */
|
/* Default implementation for _PyThreadState_GetFrame */
|
||||||
static struct _frame *
|
static struct _frame *
|
||||||
threadstate_getframe(PyThreadState *self)
|
threadstate_getframe(PyThreadState *self)
|
||||||
|
|
Loading…
Reference in New Issue