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()
Returns true if automatic collection is enabled.
int enabled;
int debug;
+ long disabled_threads;
/* linked lists of container objects */
struct gc_generation generations[NUM_GENERATIONS];
PyGC_Head *generation0;
import unittest
from test.support import (verbose, refcount_test, run_unittest,
strip_python_stderr, cpython_only, start_threads,
- temp_dir, requires_type_collecting)
+ temp_dir, requires_type_collecting,reap_threads)
from test.support.script_helper import assert_python_ok, make_script
import sys
import gc
import weakref
import threading
+import warnings
+
try:
from _testcapi import with_tp_del
# empty __dict__.
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():
enabled = gc.isenabled()
gc.disable()
--- /dev/null
+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.
gc_enable_impl(PyObject *module)
/*[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;
Py_RETURN_NONE;
}
{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 = {
PyModuleDef_HEAD_INIT,
"gc", /* m_name */
if (PyModule_AddObject(m, "callbacks", _PyRuntime.gc.callbacks) < 0)
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
ADD_INT(DEBUG_STATS);
ADD_INT(DEBUG_COLLECTABLE);