bpo-36710: Pass tstate parameter to GC collect() (GH-17267)

Pass tstate parameter (PyThreadState) to GC collect() function and
other GC subfunctions.
This commit is contained in:
Victor Stinner 2019-11-20 01:49:32 +01:00 committed by GitHub
parent 279d8df5e5
commit 2e96906da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 50 additions and 37 deletions

View File

@ -27,6 +27,7 @@
#include "pycore_context.h" #include "pycore_context.h"
#include "pycore_initconfig.h" #include "pycore_initconfig.h"
#include "pycore_object.h" #include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pymem.h" #include "pycore_pymem.h"
#include "pycore_pystate.h" #include "pycore_pystate.h"
#include "frameobject.h" /* for PyFrame_ClearFreeList */ #include "frameobject.h" /* for PyFrame_ClearFreeList */
@ -917,10 +918,11 @@ debug_cycle(const char *msg, PyObject *op)
* merged into the old list regardless. * merged into the old list regardless.
*/ */
static void static void
handle_legacy_finalizers(struct _gc_runtime_state *state, handle_legacy_finalizers(PyThreadState *tstate,
struct _gc_runtime_state *state,
PyGC_Head *finalizers, PyGC_Head *old) PyGC_Head *finalizers, PyGC_Head *old)
{ {
assert(!PyErr_Occurred()); assert(!_PyErr_Occurred(tstate));
assert(state->garbage != NULL); assert(state->garbage != NULL);
PyGC_Head *gc = GC_NEXT(finalizers); PyGC_Head *gc = GC_NEXT(finalizers);
@ -929,7 +931,7 @@ handle_legacy_finalizers(struct _gc_runtime_state *state,
if ((state->debug & DEBUG_SAVEALL) || has_legacy_finalizer(op)) { if ((state->debug & DEBUG_SAVEALL) || has_legacy_finalizer(op)) {
if (PyList_Append(state->garbage, op) < 0) { if (PyList_Append(state->garbage, op) < 0) {
PyErr_Clear(); _PyErr_Clear(tstate);
break; break;
} }
} }
@ -979,10 +981,10 @@ finalize_garbage(PyGC_Head *collectable)
* objects may be freed. It is possible I screwed something up here. * objects may be freed. It is possible I screwed something up here.
*/ */
static void static void
delete_garbage(struct _gc_runtime_state *state, delete_garbage(PyThreadState *tstate, struct _gc_runtime_state *state,
PyGC_Head *collectable, PyGC_Head *old) PyGC_Head *collectable, PyGC_Head *old)
{ {
assert(!PyErr_Occurred()); assert(!_PyErr_Occurred(tstate));
while (!gc_list_is_empty(collectable)) { while (!gc_list_is_empty(collectable)) {
PyGC_Head *gc = GC_NEXT(collectable); PyGC_Head *gc = GC_NEXT(collectable);
@ -994,7 +996,7 @@ delete_garbage(struct _gc_runtime_state *state,
if (state->debug & DEBUG_SAVEALL) { if (state->debug & DEBUG_SAVEALL) {
assert(state->garbage != NULL); assert(state->garbage != NULL);
if (PyList_Append(state->garbage, op) < 0) { if (PyList_Append(state->garbage, op) < 0) {
PyErr_Clear(); _PyErr_Clear(tstate);
} }
} }
else { else {
@ -1002,7 +1004,7 @@ delete_garbage(struct _gc_runtime_state *state,
if ((clear = Py_TYPE(op)->tp_clear) != NULL) { if ((clear = Py_TYPE(op)->tp_clear) != NULL) {
Py_INCREF(op); Py_INCREF(op);
(void) clear(op); (void) clear(op);
if (PyErr_Occurred()) { if (_PyErr_Occurred(tstate)) {
_PyErr_WriteUnraisableMsg("in tp_clear of", _PyErr_WriteUnraisableMsg("in tp_clear of",
(PyObject*)Py_TYPE(op)); (PyObject*)Py_TYPE(op));
} }
@ -1143,7 +1145,7 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
/* This is the main function. Read this to understand how the /* This is the main function. Read this to understand how the
* collection process works. */ * collection process works. */
static Py_ssize_t static Py_ssize_t
collect(struct _gc_runtime_state *state, int generation, collect(PyThreadState *tstate, struct _gc_runtime_state *state, int generation,
Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable, int nofail) Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable, int nofail)
{ {
int i; int i;
@ -1245,7 +1247,7 @@ collect(struct _gc_runtime_state *state, int generation,
* in finalizers to be freed. * in finalizers to be freed.
*/ */
m += gc_list_size(&final_unreachable); m += gc_list_size(&final_unreachable);
delete_garbage(state, &final_unreachable, old); delete_garbage(tstate, state, &final_unreachable, old);
/* Collect statistics on uncollectable objects found and print /* Collect statistics on uncollectable objects found and print
* debugging information. */ * debugging information. */
@ -1266,7 +1268,7 @@ collect(struct _gc_runtime_state *state, int generation,
* reachable list of garbage. The programmer has to deal with * reachable list of garbage. The programmer has to deal with
* this if they insist on creating this type of structure. * this if they insist on creating this type of structure.
*/ */
handle_legacy_finalizers(state, &finalizers, old); handle_legacy_finalizers(tstate, state, &finalizers, old);
validate_list(old, collecting_clear_unreachable_clear); validate_list(old, collecting_clear_unreachable_clear);
/* Clear free list only during the collection of the highest /* Clear free list only during the collection of the highest
@ -1275,9 +1277,9 @@ collect(struct _gc_runtime_state *state, int generation,
clear_freelists(); clear_freelists();
} }
if (PyErr_Occurred()) { if (_PyErr_Occurred(tstate)) {
if (nofail) { if (nofail) {
PyErr_Clear(); _PyErr_Clear(tstate);
} }
else { else {
if (gc_str == NULL) if (gc_str == NULL)
@ -1301,11 +1303,11 @@ collect(struct _gc_runtime_state *state, int generation,
stats->uncollectable += n; stats->uncollectable += n;
if (PyDTrace_GC_DONE_ENABLED()) { if (PyDTrace_GC_DONE_ENABLED()) {
PyDTrace_GC_DONE(n+m); PyDTrace_GC_DONE(n + m);
} }
assert(!PyErr_Occurred()); assert(!_PyErr_Occurred(tstate));
return n+m; return n + m;
} }
/* Invoke progress callbacks to notify clients that garbage collection /* Invoke progress callbacks to notify clients that garbage collection
@ -1356,19 +1358,20 @@ invoke_gc_callback(struct _gc_runtime_state *state, const char *phase,
* progress callbacks. * progress callbacks.
*/ */
static Py_ssize_t static Py_ssize_t
collect_with_callback(struct _gc_runtime_state *state, int generation) collect_with_callback(PyThreadState *tstate, struct _gc_runtime_state *state,
int generation)
{ {
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
Py_ssize_t result, collected, uncollectable; Py_ssize_t result, collected, uncollectable;
invoke_gc_callback(state, "start", generation, 0, 0); invoke_gc_callback(state, "start", generation, 0, 0);
result = collect(state, generation, &collected, &uncollectable, 0); result = collect(tstate, state, generation, &collected, &uncollectable, 0);
invoke_gc_callback(state, "stop", generation, collected, uncollectable); invoke_gc_callback(state, "stop", generation, collected, uncollectable);
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
return result; return result;
} }
static Py_ssize_t static Py_ssize_t
collect_generations(struct _gc_runtime_state *state) collect_generations(PyThreadState *tstate, struct _gc_runtime_state *state)
{ {
/* Find the oldest generation (highest numbered) where the count /* Find the oldest generation (highest numbered) where the count
* exceeds the threshold. Objects in the that generation and * exceeds the threshold. Objects in the that generation and
@ -1383,7 +1386,7 @@ collect_generations(struct _gc_runtime_state *state)
if (i == NUM_GENERATIONS - 1 if (i == NUM_GENERATIONS - 1
&& state->long_lived_pending < state->long_lived_total / 4) && state->long_lived_pending < state->long_lived_total / 4)
continue; continue;
n = collect_with_callback(state, i); n = collect_with_callback(tstate, state, i);
break; break;
} }
} }
@ -1451,9 +1454,10 @@ static Py_ssize_t
gc_collect_impl(PyObject *module, int generation) gc_collect_impl(PyObject *module, int generation)
/*[clinic end generated code: output=b697e633043233c7 input=40720128b682d879]*/ /*[clinic end generated code: output=b697e633043233c7 input=40720128b682d879]*/
{ {
PyThreadState *tstate = _PyThreadState_GET();
if (generation < 0 || generation >= NUM_GENERATIONS) { if (generation < 0 || generation >= NUM_GENERATIONS) {
PyErr_SetString(PyExc_ValueError, "invalid generation"); _PyErr_SetString(tstate, PyExc_ValueError, "invalid generation");
return -1; return -1;
} }
@ -1465,7 +1469,7 @@ gc_collect_impl(PyObject *module, int generation)
} }
else { else {
state->collecting = 1; state->collecting = 1;
n = collect_with_callback(state, generation); n = collect_with_callback(tstate, state, generation);
state->collecting = 0; state->collecting = 0;
} }
return n; return n;
@ -1940,7 +1944,9 @@ PyInit_gc(void)
Py_ssize_t Py_ssize_t
PyGC_Collect(void) PyGC_Collect(void)
{ {
PyThreadState *tstate = _PyThreadState_GET();
struct _gc_runtime_state *state = &_PyRuntime.gc; struct _gc_runtime_state *state = &_PyRuntime.gc;
if (!state->enabled) { if (!state->enabled) {
return 0; return 0;
} }
@ -1953,9 +1959,9 @@ PyGC_Collect(void)
else { else {
PyObject *exc, *value, *tb; PyObject *exc, *value, *tb;
state->collecting = 1; state->collecting = 1;
PyErr_Fetch(&exc, &value, &tb); _PyErr_Fetch(tstate, &exc, &value, &tb);
n = collect_with_callback(state, NUM_GENERATIONS - 1); n = collect_with_callback(tstate, state, NUM_GENERATIONS - 1);
PyErr_Restore(exc, value, tb); _PyErr_Restore(tstate, exc, value, tb);
state->collecting = 0; state->collecting = 0;
} }
@ -1971,7 +1977,8 @@ _PyGC_CollectIfEnabled(void)
Py_ssize_t Py_ssize_t
_PyGC_CollectNoFail(void) _PyGC_CollectNoFail(void)
{ {
assert(!PyErr_Occurred()); PyThreadState *tstate = _PyThreadState_GET();
assert(!_PyErr_Occurred(tstate));
struct _gc_runtime_state *state = &_PyRuntime.gc; struct _gc_runtime_state *state = &_PyRuntime.gc;
Py_ssize_t n; Py_ssize_t n;
@ -1987,7 +1994,7 @@ _PyGC_CollectNoFail(void)
} }
else { else {
state->collecting = 1; state->collecting = 1;
n = collect(state, NUM_GENERATIONS - 1, NULL, NULL, 1); n = collect(tstate, state, NUM_GENERATIONS - 1, NULL, NULL, 1);
state->collecting = 0; state->collecting = 0;
} }
return n; return n;
@ -2098,19 +2105,23 @@ static PyObject *
_PyObject_GC_Alloc(int use_calloc, size_t basicsize) _PyObject_GC_Alloc(int use_calloc, size_t basicsize)
{ {
struct _gc_runtime_state *state = &_PyRuntime.gc; struct _gc_runtime_state *state = &_PyRuntime.gc;
PyObject *op; if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) {
return PyErr_NoMemory();
}
size_t size = sizeof(PyGC_Head) + basicsize;
PyGC_Head *g; PyGC_Head *g;
size_t size; if (use_calloc) {
if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head))
return PyErr_NoMemory();
size = sizeof(PyGC_Head) + basicsize;
if (use_calloc)
g = (PyGC_Head *)PyObject_Calloc(1, size); g = (PyGC_Head *)PyObject_Calloc(1, size);
else }
else {
g = (PyGC_Head *)PyObject_Malloc(size); g = (PyGC_Head *)PyObject_Malloc(size);
if (g == NULL) }
if (g == NULL) {
return PyErr_NoMemory(); return PyErr_NoMemory();
}
assert(((uintptr_t)g & 3) == 0); // g must be aligned 4bytes boundary assert(((uintptr_t)g & 3) == 0); // g must be aligned 4bytes boundary
g->_gc_next = 0; g->_gc_next = 0;
g->_gc_prev = 0; g->_gc_prev = 0;
state->generations[0].count++; /* number of allocated GC objects */ state->generations[0].count++; /* number of allocated GC objects */
@ -2118,12 +2129,14 @@ _PyObject_GC_Alloc(int use_calloc, size_t basicsize)
state->enabled && state->enabled &&
state->generations[0].threshold && state->generations[0].threshold &&
!state->collecting && !state->collecting &&
!PyErr_Occurred()) { !PyErr_Occurred())
{
PyThreadState *tstate = _PyThreadState_GET();
state->collecting = 1; state->collecting = 1;
collect_generations(state); collect_generations(tstate, state);
state->collecting = 0; state->collecting = 0;
} }
op = FROM_GC(g); PyObject *op = FROM_GC(g);
return op; return op;
} }