aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python
diff options
context:
space:
mode:
Diffstat (limited to 'Python')
-rw-r--r--Python/ceval.c10
-rw-r--r--Python/gc.c25
-rw-r--r--Python/gc_free_threading.c20
-rw-r--r--Python/initconfig.c2
-rw-r--r--Python/pylifecycle.c14
5 files changed, 52 insertions, 19 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index 50665defd38..291e753dec0 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -627,12 +627,14 @@ _PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys)
PyObject *seen = NULL;
PyObject *dummy = NULL;
PyObject *values = NULL;
- PyObject *get = NULL;
// We use the two argument form of map.get(key, default) for two reasons:
// - Atomically check for a key and get its value without error handling.
// - Don't cause key creation or resizing in dict subclasses like
// collections.defaultdict that define __missing__ (or similar).
- int meth_found = _PyObject_GetMethod(map, &_Py_ID(get), &get);
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
+ int meth_found = _PyObject_GetMethodStackRef(tstate, map, &_Py_ID(get), &cref.ref);
+ PyObject *get = PyStackRef_AsPyObjectBorrow(cref.ref);
if (get == NULL) {
goto fail;
}
@@ -682,12 +684,12 @@ _PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys)
}
// Success:
done:
- Py_DECREF(get);
+ _PyThreadState_PopCStackRef(tstate, &cref);
Py_DECREF(seen);
Py_DECREF(dummy);
return values;
fail:
- Py_XDECREF(get);
+ _PyThreadState_PopCStackRef(tstate, &cref);
Py_XDECREF(seen);
Py_XDECREF(dummy);
Py_XDECREF(values);
diff --git a/Python/gc.c b/Python/gc.c
index 02135a3fb44..88849a43680 100644
--- a/Python/gc.c
+++ b/Python/gc.c
@@ -870,7 +870,7 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
* no object in `unreachable` is weakly referenced anymore.
*/
static int
-handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
+handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks)
{
PyGC_Head *gc;
PyObject *op; /* generally FROM_GC(gc) */
@@ -879,7 +879,9 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
PyGC_Head *next;
int num_freed = 0;
- gc_list_init(&wrcb_to_call);
+ if (allow_callbacks) {
+ gc_list_init(&wrcb_to_call);
+ }
/* Clear all weakrefs to the objects in unreachable. If such a weakref
* also has a callback, move it into `wrcb_to_call` if the callback
@@ -935,6 +937,11 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == op);
_PyWeakref_ClearRef(wr);
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
+
+ if (!allow_callbacks) {
+ continue;
+ }
+
if (wr->wr_callback == NULL) {
/* no callback */
continue;
@@ -987,6 +994,10 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
}
}
+ if (!allow_callbacks) {
+ return 0;
+ }
+
/* Invoke the callbacks we decided to honor. It's safe to invoke them
* because they can't reference unreachable objects.
*/
@@ -1737,7 +1748,7 @@ gc_collect_region(PyThreadState *tstate,
}
/* Clear weakrefs and invoke callbacks as necessary. */
- stats->collected += handle_weakrefs(&unreachable, to);
+ stats->collected += handle_weakrefs(&unreachable, to, true);
gc_list_validate_space(to, gcstate->visited_space);
validate_list(to, collecting_clear_unreachable_clear);
validate_list(&unreachable, collecting_set_unreachable_clear);
@@ -1751,6 +1762,14 @@ gc_collect_region(PyThreadState *tstate,
gc_list_init(&final_unreachable);
handle_resurrected_objects(&unreachable, &final_unreachable, to);
+ /* Clear weakrefs to objects in the unreachable set. No Python-level
+ * code must be allowed to access those unreachable objects. During
+ * delete_garbage(), finalizers outside the unreachable set might run
+ * and create new weakrefs. If those weakrefs were not cleared, they
+ * could reveal unreachable objects. Callbacks are not executed.
+ */
+ handle_weakrefs(&final_unreachable, NULL, false);
+
/* Call tp_clear on objects in the final_unreachable set. This will cause
* the reference cycles to be broken. It may also cause some objects
* in finalizers to be freed.
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 5aaa68c5b51..d46598b23b3 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -1492,9 +1492,9 @@ move_legacy_finalizer_reachable(struct collection_state *state)
}
// Clear all weakrefs to unreachable objects. Weakrefs with callbacks are
-// enqueued in `wrcb_to_call`, but not invoked yet.
+// optionally enqueued in `wrcb_to_call`, but not invoked yet.
static void
-clear_weakrefs(struct collection_state *state)
+clear_weakrefs(struct collection_state *state, bool enqueue_callbacks)
{
PyObject *op;
WORKSTACK_FOR_EACH(&state->unreachable, op) {
@@ -1526,6 +1526,10 @@ clear_weakrefs(struct collection_state *state)
_PyWeakref_ClearRef(wr);
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
+ if (!enqueue_callbacks) {
+ continue;
+ }
+
// We do not invoke callbacks for weakrefs that are themselves
// unreachable. This is partly for historical reasons: weakrefs
// predate safe object finalization, and a weakref that is itself
@@ -2211,7 +2215,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
interp->gc.long_lived_total = state->long_lived_total;
// Clear weakrefs and enqueue callbacks (but do not call them).
- clear_weakrefs(state);
+ clear_weakrefs(state, true);
_PyEval_StartTheWorld(interp);
// Deallocate any object from the refcount merge step
@@ -2222,11 +2226,19 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
call_weakref_callbacks(state);
finalize_garbage(state);
- // Handle any objects that may have resurrected after the finalization.
_PyEval_StopTheWorld(interp);
+ // Handle any objects that may have resurrected after the finalization.
err = handle_resurrected_objects(state);
// Clear free lists in all threads
_PyGC_ClearAllFreeLists(interp);
+ if (err == 0) {
+ // Clear weakrefs to objects in the unreachable set. No Python-level
+ // code must be allowed to access those unreachable objects. During
+ // delete_garbage(), finalizers outside the unreachable set might
+ // run and create new weakrefs. If those weakrefs were not cleared,
+ // they could reveal unreachable objects.
+ clear_weakrefs(state, false);
+ }
_PyEval_StartTheWorld(interp);
if (err < 0) {
diff --git a/Python/initconfig.c b/Python/initconfig.c
index 71d7cfed5c4..73a9a9bf1ca 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -312,7 +312,7 @@ The following implementation-specific options are available:\n\
"-X gil=[0|1]: enable (1) or disable (0) the GIL; also PYTHON_GIL\n"
#endif
"\
--X importtime[=2]: show how long each import takes; use -X importtime=2 to\
+-X importtime[=2]: show how long each import takes; use -X importtime=2 to\n\
log imports of already-loaded modules; also PYTHONPROFILEIMPORTTIME\n\
-X int_max_str_digits=N: limit the size of int<->str conversions;\n\
0 disables the limit; also PYTHONINTMAXSTRDIGITS\n\
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 724fda63511..00e8d030765 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1702,8 +1702,10 @@ finalize_modules(PyThreadState *tstate)
#endif
// Stop watching __builtin__ modifications
- PyDict_Unwatch(0, interp->builtins);
-
+ if (PyDict_Unwatch(0, interp->builtins) < 0) {
+ // might happen if interp is cleared before watching the __builtin__
+ PyErr_Clear();
+ }
PyObject *modules = _PyImport_GetModules(interp);
if (modules == NULL) {
// Already done
@@ -2377,15 +2379,13 @@ new_interpreter(PyThreadState **tstate_p,
error:
*tstate_p = NULL;
if (tstate != NULL) {
- PyThreadState_Clear(tstate);
- _PyThreadState_Detach(tstate);
- PyThreadState_Delete(tstate);
+ Py_EndInterpreter(tstate);
+ } else {
+ PyInterpreterState_Delete(interp);
}
if (save_tstate != NULL) {
_PyThreadState_Attach(save_tstate);
}
- PyInterpreterState_Delete(interp);
-
return status;
}