mirror of https://github.com/python/cpython
Revert "bpo-31356: Add context manager to temporarily disable GC GH-5495
This reverts commit 72a0d218dc
.
The reverted commit had a few issues so it was unanimously decided
to undo it. See the bpo issue for details.
This commit is contained in:
parent
aa0735f597
commit
383b32fe10
|
@ -33,34 +33,6 @@ The :mod:`gc` module provides the following functions:
|
||||||
Disable automatic garbage collection.
|
Disable automatic garbage collection.
|
||||||
|
|
||||||
|
|
||||||
.. class:: ensure_disabled()
|
|
||||||
|
|
||||||
Return a context manager object that disables the garbage collector and reenables the previous
|
|
||||||
state upon completion of the block. This is basically equivalent to::
|
|
||||||
|
|
||||||
from gc import enable, disable, isenabled
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def ensure_disabled():
|
|
||||||
was_enabled_previously = isenabled()
|
|
||||||
gc.disable()
|
|
||||||
yield
|
|
||||||
if was_enabled_previously:
|
|
||||||
gc.enable()
|
|
||||||
|
|
||||||
And lets you write code like this::
|
|
||||||
|
|
||||||
with ensure_disabled():
|
|
||||||
run_some_timing()
|
|
||||||
|
|
||||||
with ensure_disabled():
|
|
||||||
# do_something_that_has_real_time_guarantees
|
|
||||||
# such as a pair trade, robotic braking, etc
|
|
||||||
|
|
||||||
without needing to explicitly enable and disable the garbage collector yourself.
|
|
||||||
This context manager is implemented in C to assure atomicity, thread safety and speed.
|
|
||||||
|
|
||||||
|
|
||||||
.. function:: isenabled()
|
.. function:: isenabled()
|
||||||
|
|
||||||
Returns true if automatic collection is enabled.
|
Returns true if automatic collection is enabled.
|
||||||
|
|
|
@ -116,7 +116,6 @@ struct _gc_runtime_state {
|
||||||
|
|
||||||
int enabled;
|
int enabled;
|
||||||
int debug;
|
int debug;
|
||||||
long disabled_threads;
|
|
||||||
/* linked lists of container objects */
|
/* linked lists of container objects */
|
||||||
struct gc_generation generations[NUM_GENERATIONS];
|
struct gc_generation generations[NUM_GENERATIONS];
|
||||||
PyGC_Head *generation0;
|
PyGC_Head *generation0;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
from test.support import (verbose, refcount_test, run_unittest,
|
from test.support import (verbose, refcount_test, run_unittest,
|
||||||
strip_python_stderr, cpython_only, start_threads,
|
strip_python_stderr, cpython_only, start_threads,
|
||||||
temp_dir, requires_type_collecting,reap_threads)
|
temp_dir, requires_type_collecting)
|
||||||
from test.support.script_helper import assert_python_ok, make_script
|
from test.support.script_helper import assert_python_ok, make_script
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -9,8 +9,6 @@ import time
|
||||||
import gc
|
import gc
|
||||||
import weakref
|
import weakref
|
||||||
import threading
|
import threading
|
||||||
import warnings
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from _testcapi import with_tp_del
|
from _testcapi import with_tp_del
|
||||||
|
@ -1009,73 +1007,6 @@ class GCTogglingTests(unittest.TestCase):
|
||||||
# empty __dict__.
|
# empty __dict__.
|
||||||
self.assertEqual(x, None)
|
self.assertEqual(x, None)
|
||||||
|
|
||||||
def test_ensure_disabled(self):
|
|
||||||
original_status = gc.isenabled()
|
|
||||||
|
|
||||||
with gc.ensure_disabled():
|
|
||||||
inside_status = gc.isenabled()
|
|
||||||
|
|
||||||
after_status = gc.isenabled()
|
|
||||||
self.assertEqual(original_status, True)
|
|
||||||
self.assertEqual(inside_status, False)
|
|
||||||
self.assertEqual(after_status, True)
|
|
||||||
|
|
||||||
def test_ensure_disabled_with_gc_disabled(self):
|
|
||||||
gc.disable()
|
|
||||||
|
|
||||||
original_status = gc.isenabled()
|
|
||||||
|
|
||||||
with gc.ensure_disabled():
|
|
||||||
inside_status = gc.isenabled()
|
|
||||||
|
|
||||||
after_status = gc.isenabled()
|
|
||||||
self.assertEqual(original_status, False)
|
|
||||||
self.assertEqual(inside_status, False)
|
|
||||||
self.assertEqual(after_status, False)
|
|
||||||
|
|
||||||
@reap_threads
|
|
||||||
def test_ensure_disabled_thread(self):
|
|
||||||
|
|
||||||
thread_original_status = None
|
|
||||||
thread_inside_status = None
|
|
||||||
thread_after_status = None
|
|
||||||
|
|
||||||
def disabling_thread():
|
|
||||||
nonlocal thread_original_status
|
|
||||||
nonlocal thread_inside_status
|
|
||||||
nonlocal thread_after_status
|
|
||||||
thread_original_status = gc.isenabled()
|
|
||||||
|
|
||||||
with gc.ensure_disabled():
|
|
||||||
time.sleep(0.01)
|
|
||||||
thread_inside_status = gc.isenabled()
|
|
||||||
|
|
||||||
thread_after_status = gc.isenabled()
|
|
||||||
|
|
||||||
original_status = gc.isenabled()
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as w, gc.ensure_disabled():
|
|
||||||
inside_status_before_thread = gc.isenabled()
|
|
||||||
thread = threading.Thread(target=disabling_thread)
|
|
||||||
thread.start()
|
|
||||||
inside_status_after_thread = gc.isenabled()
|
|
||||||
|
|
||||||
after_status = gc.isenabled()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
self.assertEqual(len(w), 1)
|
|
||||||
self.assertTrue(issubclass(w[-1].category, RuntimeWarning))
|
|
||||||
self.assertEqual("Garbage collector enabled while another thread is "
|
|
||||||
"inside gc.ensure_enabled", str(w[-1].message))
|
|
||||||
self.assertEqual(original_status, True)
|
|
||||||
self.assertEqual(inside_status_before_thread, False)
|
|
||||||
self.assertEqual(thread_original_status, False)
|
|
||||||
self.assertEqual(thread_inside_status, True)
|
|
||||||
self.assertEqual(thread_after_status, False)
|
|
||||||
self.assertEqual(inside_status_after_thread, False)
|
|
||||||
self.assertEqual(after_status, True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
enabled = gc.isenabled()
|
enabled = gc.isenabled()
|
||||||
gc.disable()
|
gc.disable()
|
||||||
|
|
|
@ -196,17 +196,6 @@ by Sanyam Khurana.
|
||||||
|
|
||||||
..
|
..
|
||||||
|
|
||||||
.. bpo: 31356
|
|
||||||
.. date: 2017-11-02-00-34-42
|
|
||||||
.. nonce: 54Lb8U
|
|
||||||
.. section: Core and Builtins
|
|
||||||
|
|
||||||
Add a new contextmanager to the gc module that temporarily disables the GC
|
|
||||||
and restores the previous state. The implementation is done in C to assure
|
|
||||||
atomicity and speed.
|
|
||||||
|
|
||||||
..
|
|
||||||
|
|
||||||
.. bpo: 31179
|
.. bpo: 31179
|
||||||
.. date: 2017-08-10-17-32-48
|
.. date: 2017-08-10-17-32-48
|
||||||
.. nonce: XcgLYI
|
.. nonce: XcgLYI
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Remove the new API added in bpo-31356 (gc.ensure_disabled() context
|
||||||
|
manager).
|
|
@ -1067,10 +1067,6 @@ static PyObject *
|
||||||
gc_enable_impl(PyObject *module)
|
gc_enable_impl(PyObject *module)
|
||||||
/*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
|
/*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
|
||||||
{
|
{
|
||||||
if(_PyRuntime.gc.disabled_threads){
|
|
||||||
PyErr_WarnEx(PyExc_RuntimeWarning, "Garbage collector enabled while another "
|
|
||||||
"thread is inside gc.ensure_enabled",1);
|
|
||||||
}
|
|
||||||
_PyRuntime.gc.enabled = 1;
|
_PyRuntime.gc.enabled = 1;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
@ -1512,102 +1508,6 @@ static PyMethodDef GcMethods[] = {
|
||||||
{NULL, NULL} /* Sentinel */
|
{NULL, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD
|
|
||||||
int previous_gc_state;
|
|
||||||
} ensure_disabled_object;
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
ensure_disabled_object_dealloc(ensure_disabled_object *m_obj)
|
|
||||||
{
|
|
||||||
Py_TYPE(m_obj)->tp_free((PyObject*)m_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
ensure_disabled__enter__method(ensure_disabled_object *self, PyObject *args)
|
|
||||||
{
|
|
||||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
|
||||||
++_PyRuntime.gc.disabled_threads;
|
|
||||||
self->previous_gc_state = _PyRuntime.gc.enabled;
|
|
||||||
gc_disable_impl(NULL);
|
|
||||||
PyGILState_Release(gstate);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
ensure_disabled__exit__method(ensure_disabled_object *self, PyObject *args)
|
|
||||||
{
|
|
||||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
|
||||||
--_PyRuntime.gc.disabled_threads;
|
|
||||||
if(self->previous_gc_state){
|
|
||||||
gc_enable_impl(NULL);
|
|
||||||
}else{
|
|
||||||
gc_disable_impl(NULL);
|
|
||||||
}
|
|
||||||
PyGILState_Release(gstate);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static struct PyMethodDef ensure_disabled_object_methods[] = {
|
|
||||||
{"__enter__", (PyCFunction) ensure_disabled__enter__method, METH_NOARGS},
|
|
||||||
{"__exit__", (PyCFunction) ensure_disabled__exit__method, METH_VARARGS},
|
|
||||||
{NULL, NULL} /* sentinel */
|
|
||||||
};
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
new_disabled_obj(PyTypeObject *type, PyObject *args, PyObject *kwdict){
|
|
||||||
ensure_disabled_object *self;
|
|
||||||
self = (ensure_disabled_object *)type->tp_alloc(type, 0);
|
|
||||||
return (PyObject *) self;
|
|
||||||
};
|
|
||||||
|
|
||||||
static PyTypeObject gc_ensure_disabled_type = {
|
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
"gc.ensure_disabled", /* tp_name */
|
|
||||||
sizeof(ensure_disabled_object), /* tp_size */
|
|
||||||
0, /* tp_itemsize */
|
|
||||||
/* methods */
|
|
||||||
(destructor) ensure_disabled_object_dealloc,/* tp_dealloc */
|
|
||||||
0, /* tp_print */
|
|
||||||
0, /* tp_getattr */
|
|
||||||
0, /* tp_setattr */
|
|
||||||
0, /* tp_reserved */
|
|
||||||
0, /* tp_repr */
|
|
||||||
0, /* tp_as_number */
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
PyObject_GenericGetAttr, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /* tp_traverse */
|
|
||||||
0, /* tp_clear */
|
|
||||||
0, /* tp_richcompare */
|
|
||||||
0, /* tp_weaklistoffset */
|
|
||||||
0, /* tp_iter */
|
|
||||||
0, /* tp_iternext */
|
|
||||||
ensure_disabled_object_methods, /* tp_methods */
|
|
||||||
0, /* tp_members */
|
|
||||||
0, /* tp_getset */
|
|
||||||
0, /* tp_base */
|
|
||||||
0, /* tp_dict */
|
|
||||||
0, /* tp_descr_get */
|
|
||||||
0, /* tp_descr_set */
|
|
||||||
0, /* tp_dictoffset */
|
|
||||||
0, /* tp_init */
|
|
||||||
PyType_GenericAlloc, /* tp_alloc */
|
|
||||||
new_disabled_obj, /* tp_new */
|
|
||||||
PyObject_Del, /* tp_free */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static struct PyModuleDef gcmodule = {
|
static struct PyModuleDef gcmodule = {
|
||||||
PyModuleDef_HEAD_INIT,
|
PyModuleDef_HEAD_INIT,
|
||||||
"gc", /* m_name */
|
"gc", /* m_name */
|
||||||
|
@ -1648,12 +1548,6 @@ PyInit_gc(void)
|
||||||
if (PyModule_AddObject(m, "callbacks", _PyRuntime.gc.callbacks) < 0)
|
if (PyModule_AddObject(m, "callbacks", _PyRuntime.gc.callbacks) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (PyType_Ready(&gc_ensure_disabled_type) < 0)
|
|
||||||
return NULL;
|
|
||||||
if (PyModule_AddObject(m, "ensure_disabled", (PyObject*) &gc_ensure_disabled_type) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
|
|
||||||
#define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) return NULL
|
#define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) return NULL
|
||||||
ADD_INT(DEBUG_STATS);
|
ADD_INT(DEBUG_STATS);
|
||||||
ADD_INT(DEBUG_COLLECTABLE);
|
ADD_INT(DEBUG_COLLECTABLE);
|
||||||
|
|
Loading…
Reference in New Issue