Skip to content

Revert "bpo-31356: Add context manager to temporarily disable GC #4224 #5495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions Doc/library/gc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,34 +33,6 @@ The :mod:`gc` module provides the following functions:
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.
Expand Down
1 change: 0 additions & 1 deletion Include/internal/mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ struct _gc_runtime_state {

int enabled;
int debug;
long disabled_threads;
/* linked lists of container objects */
struct gc_generation generations[NUM_GENERATIONS];
PyGC_Head *generation0;
Expand Down
71 changes: 1 addition & 70 deletions Lib/test/test_gc.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import unittest
from test.support import (verbose, refcount_test, run_unittest,
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

import sys
import time
import gc
import weakref
import threading
import warnings


try:
from _testcapi import with_tp_del
Expand Down Expand Up @@ -1009,73 +1007,6 @@ def __del__(self):
# 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()
Expand Down
11 changes: 0 additions & 11 deletions Misc/NEWS.d/3.7.0b1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
.. date: 2017-08-10-17-32-48
.. nonce: XcgLYI
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Remove the new API added in bpo-31356 (gc.ensure_disabled() context
manager).
106 changes: 0 additions & 106 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1067,10 +1067,6 @@ static PyObject *
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;
}
Expand Down Expand Up @@ -1512,102 +1508,6 @@ static PyMethodDef GcMethods[] = {
{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 */
Expand Down Expand Up @@ -1648,12 +1548,6 @@ PyInit_gc(void)
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);
Expand Down