From 825efeed468afc6b6be3bc20abea7955847e36a8 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 15 Jun 2024 16:57:58 +0800 Subject: [PATCH 1/5] add test --- Lib/test/test_free_threading/test_frame.py | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Lib/test/test_free_threading/test_frame.py diff --git a/Lib/test/test_free_threading/test_frame.py b/Lib/test/test_free_threading/test_frame.py new file mode 100644 index 00000000000000..34a6e8e4b6d27c --- /dev/null +++ b/Lib/test/test_free_threading/test_frame.py @@ -0,0 +1,31 @@ +import sys +import unittest +import threading + +from test.support import threading_helper + +NTHREADS = 6 + +@threading_helper.requires_working_threading() +class TestFrame(unittest.TestCase): + def test_frame_clear_simultaneous(self): + + def gen(): + for _ in range(10000): + return sys._getframe() + + foo = gen() + def work(): + for _ in range(4000): + frame1 = foo + frame1.clear() + + + threads = [] + for i in range(NTHREADS): + thread = threading.Thread(target=work) + thread.start() + threads.append(thread) + + for thread in threads: + thread.join() \ No newline at end of file From c4966e26e620fcd4b5ee3e5df17d7c71671ad072 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 15 Jun 2024 16:59:25 +0800 Subject: [PATCH 2/5] Make frame clearing thread safe --- Objects/frameobject.c | 15 +++++++++++++-- Objects/genobject.c | 12 +++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 5c65007dae46d2..74d2f5d2411661 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -7,7 +7,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetDict() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_opcode_metadata.h" // _PyOpcode_Deopt, _PyOpcode_Caches - +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION #include "frameobject.h" // PyFrameObject #include "pycore_frame.h" @@ -1662,7 +1662,7 @@ frame_tp_clear(PyFrameObject *f) } static PyObject * -frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) +frame_clear_unlocked(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) { if (f->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { PyGenObject *gen = _PyFrame_GetGenerator(f->f_frame); @@ -1692,6 +1692,17 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) return NULL; } +static PyObject * +frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) +{ + PyObject *res; + // Another materialized frame might be racing on clearing the frame. + Py_BEGIN_CRITICAL_SECTION(f); + res = frame_clear_unlocked(f, NULL); + Py_END_CRITICAL_SECTION(); + return res; +} + PyDoc_STRVAR(clear__doc__, "F.clear(): clear most references held by the frame"); diff --git a/Objects/genobject.c b/Objects/genobject.c index 92cd8c61e7e9ca..2a59e9451d4e53 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -14,6 +14,7 @@ #include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION #include "pystats.h" @@ -119,7 +120,7 @@ _PyGen_Finalize(PyObject *self) } static void -gen_dealloc(PyGenObject *gen) +gen_dealloc_no_lock(PyGenObject *gen) { PyObject *self = (PyObject *) gen; @@ -158,6 +159,15 @@ gen_dealloc(PyGenObject *gen) PyObject_GC_Del(gen); } +static void +gen_dealloc(PyGenObject *gen) +{ + // Another generator's finalizer might race on clearing the frame. + Py_BEGIN_CRITICAL_SECTION(gen); + gen_dealloc_no_lock(gen); + Py_END_CRITICAL_SECTION(); +} + static PySendResult gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc, int closing) From fc7510b1417f09e335799df52fb67cb7f4327ca3 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 15 Jun 2024 17:03:29 +0800 Subject: [PATCH 3/5] Fix EOF --- Lib/test/test_free_threading/test_frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_free_threading/test_frame.py b/Lib/test/test_free_threading/test_frame.py index 34a6e8e4b6d27c..d7d1a13955eb70 100644 --- a/Lib/test/test_free_threading/test_frame.py +++ b/Lib/test/test_free_threading/test_frame.py @@ -28,4 +28,4 @@ def work(): threads.append(thread) for thread in threads: - thread.join() \ No newline at end of file + thread.join() From 21da242b5812929e259f0910f2f5a53ff43ce0cd Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:51:21 +0800 Subject: [PATCH 4/5] Cleanup test --- Lib/test/test_free_threading/test_frame.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_free_threading/test_frame.py b/Lib/test/test_free_threading/test_frame.py index d7d1a13955eb70..7ddd8c6b253a62 100644 --- a/Lib/test/test_free_threading/test_frame.py +++ b/Lib/test/test_free_threading/test_frame.py @@ -10,15 +10,13 @@ class TestFrame(unittest.TestCase): def test_frame_clear_simultaneous(self): - def gen(): - for _ in range(10000): - return sys._getframe() + def getframe(): + return sys._getframe() - foo = gen() + foo = getframe() def work(): for _ in range(4000): - frame1 = foo - frame1.clear() + foo.clear() threads = [] From acc46444a5a949e004bc956fbb1d3ba4ee748970 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:40:02 +0800 Subject: [PATCH 5/5] Revert generator fixes --- Objects/genobject.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Objects/genobject.c b/Objects/genobject.c index 2a59e9451d4e53..92cd8c61e7e9ca 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -14,7 +14,6 @@ #include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pystate.h" // _PyThreadState_GET() -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION #include "pystats.h" @@ -120,7 +119,7 @@ _PyGen_Finalize(PyObject *self) } static void -gen_dealloc_no_lock(PyGenObject *gen) +gen_dealloc(PyGenObject *gen) { PyObject *self = (PyObject *) gen; @@ -159,15 +158,6 @@ gen_dealloc_no_lock(PyGenObject *gen) PyObject_GC_Del(gen); } -static void -gen_dealloc(PyGenObject *gen) -{ - // Another generator's finalizer might race on clearing the frame. - Py_BEGIN_CRITICAL_SECTION(gen); - gen_dealloc_no_lock(gen); - Py_END_CRITICAL_SECTION(); -} - static PySendResult gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc, int closing)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: