diff --git a/src/plone.server/CHANGELOG.rst b/src/plone.server/CHANGELOG.rst index 3b48d41..7de18d2 100644 --- a/src/plone.server/CHANGELOG.rst +++ b/src/plone.server/CHANGELOG.rst @@ -1,6 +1,9 @@ 1.0a16 (unreleased) ------------------- +- Fix memory leak in get_current_request + [vangheem] + - Be able to provide `aiohttp_settings` in config.json to configure parts of aiohttp application [vangheem] diff --git a/src/plone.server/plone/server/optimizations.c b/src/plone.server/plone/server/optimizations.c index 2457ec5..1ab4a60 100644 --- a/src/plone.server/plone/server/optimizations.c +++ b/src/plone.server/plone/server/optimizations.c @@ -17,6 +17,8 @@ current_request() if (PyFrame_FastToLocalsWithError(f) < 0) return NULL; + Py_INCREF(f->f_locals); + if (PyDict_CheckExact(f->f_locals)) { self = PyDict_GetItem(f->f_locals, PyUnicode_FromString("self")); @@ -25,14 +27,16 @@ current_request() if (PyObject_HasAttr(self, PyUnicode_FromString("request"))) { found = 1; request = PyObject_GetAttr(self, PyUnicode_FromString("request")); - } + } if (PyObject_IsInstance(self, RequestHandler)) { found = 1; request = PyDict_GetItem(f->f_locals, PyUnicode_FromString("request")); + Py_INCREF(request); } } - } + + Py_DECCREF(f->f_locals); f = f->f_back; } @@ -43,7 +47,6 @@ current_request() return NULL; } - Py_INCREF(request); return (PyObject*)request; } @@ -95,4 +98,4 @@ PyInit_optimizations(void) Py_DECREF(m); return ob; -} \ No newline at end of file +} diff --git a/src/plone.server/plone/server/tests/test_utils.py b/src/plone.server/plone/server/tests/test_utils.py index 8846794..71f1262 100644 --- a/src/plone.server/plone/server/tests/test_utils.py +++ b/src/plone.server/plone/server/tests/test_utils.py @@ -1,4 +1,9 @@ from plone.server import utils +from plone.server.testing import FakeRequest +from plone.server.transactions import get_current_request + +import gc +import resource def test_module_resolve_path(): @@ -6,3 +11,23 @@ def test_module_resolve_path(): assert utils.resolve_module_path('plone.server.tests') == 'plone.server.tests' assert utils.resolve_module_path('..test_queue') == 'plone.server.tests.test_queue' assert utils.resolve_module_path('....api') == 'plone.server.api' + + +class TestGetCurrentRequest: + def test_gcr_memory(self): + self.request = FakeRequest() + + count = 0 + current = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024.0 / 1024.0 + while True: + count += 1 + get_current_request() + + if count % 1000000 == 0: + break + + if count % 100000 == 0: + gc.collect() + new = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024.0 / 1024.0 + if new - current > 10: # memory leak, this shouldn't happen + assert new == current