mirror of https://github.com/python/cpython
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:
parent
279d8df5e5
commit
2e96906da7
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue