Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f3cffd2

Browse filesBrowse files
Dino Viehland1st1
authored andcommitted
bpo-30604: clean up co_extra support (#2144)
bpo-30604: port fix from 3.6 dropping binary compatibility tweaks
1 parent c90e960 commit f3cffd2
Copy full SHA for f3cffd2

File tree

Expand file treeCollapse file tree

6 files changed

+130
-23
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+130
-23
lines changed

‎Include/pystate.h

Copy file name to clipboardExpand all lines: Include/pystate.h
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ typedef struct _is {
7474
PyObject *import_func;
7575
/* Initialized to PyEval_EvalFrameDefault(). */
7676
_PyFrameEvalFunction eval_frame;
77+
78+
Py_ssize_t co_extra_user_count;
79+
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
80+
7781
#ifdef HAVE_FORK
7882
PyObject *before_forkers;
7983
PyObject *after_forkers_parent;
@@ -173,9 +177,6 @@ typedef struct _ts {
173177
PyObject *coroutine_wrapper;
174178
int in_coroutine_wrapper;
175179

176-
Py_ssize_t co_extra_user_count;
177-
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
178-
179180
PyObject *async_gen_firstiter;
180181
PyObject *async_gen_finalizer;
181182

‎Lib/test/test_code.py

Copy file name to clipboardExpand all lines: Lib/test/test_code.py
+100-3Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,11 @@
103103
"""
104104

105105
import sys
106+
import threading
106107
import unittest
107108
import weakref
108-
from test.support import run_doctest, run_unittest, cpython_only
109+
from test.support import (run_doctest, run_unittest, cpython_only,
110+
check_impl_detail)
109111

110112

111113
def consts(t):
@@ -212,11 +214,106 @@ def callback(code):
212214
self.assertTrue(self.called)
213215

214216

217+
if check_impl_detail(cpython=True):
218+
import ctypes
219+
py = ctypes.pythonapi
220+
freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)
221+
222+
RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex
223+
RequestCodeExtraIndex.argtypes = (freefunc,)
224+
RequestCodeExtraIndex.restype = ctypes.c_ssize_t
225+
226+
SetExtra = py._PyCode_SetExtra
227+
SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp)
228+
SetExtra.restype = ctypes.c_int
229+
230+
GetExtra = py._PyCode_GetExtra
231+
GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t,
232+
ctypes.POINTER(ctypes.c_voidp))
233+
GetExtra.restype = ctypes.c_int
234+
235+
LAST_FREED = None
236+
def myfree(ptr):
237+
global LAST_FREED
238+
LAST_FREED = ptr
239+
240+
FREE_FUNC = freefunc(myfree)
241+
FREE_INDEX = RequestCodeExtraIndex(FREE_FUNC)
242+
243+
class CoExtra(unittest.TestCase):
244+
def get_func(self):
245+
# Defining a function causes the containing function to have a
246+
# reference to the code object. We need the code objects to go
247+
# away, so we eval a lambda.
248+
return eval('lambda:42')
249+
250+
def test_get_non_code(self):
251+
f = self.get_func()
252+
253+
self.assertRaises(SystemError, SetExtra, 42, FREE_INDEX,
254+
ctypes.c_voidp(100))
255+
self.assertRaises(SystemError, GetExtra, 42, FREE_INDEX,
256+
ctypes.c_voidp(100))
257+
258+
def test_bad_index(self):
259+
f = self.get_func()
260+
self.assertRaises(SystemError, SetExtra, f.__code__,
261+
FREE_INDEX+100, ctypes.c_voidp(100))
262+
self.assertEqual(GetExtra(f.__code__, FREE_INDEX+100,
263+
ctypes.c_voidp(100)), 0)
264+
265+
def test_free_called(self):
266+
# Verify that the provided free function gets invoked
267+
# when the code object is cleaned up.
268+
f = self.get_func()
269+
270+
SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(100))
271+
del f
272+
self.assertEqual(LAST_FREED, 100)
273+
274+
def test_get_set(self):
275+
# Test basic get/set round tripping.
276+
f = self.get_func()
277+
278+
extra = ctypes.c_voidp()
279+
280+
SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(200))
281+
# reset should free...
282+
SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(300))
283+
self.assertEqual(LAST_FREED, 200)
284+
285+
extra = ctypes.c_voidp()
286+
GetExtra(f.__code__, FREE_INDEX, extra)
287+
self.assertEqual(extra.value, 300)
288+
del f
289+
290+
def test_free_different_thread(self):
291+
# Freeing a code object on a different thread then
292+
# where the co_extra was set should be safe.
293+
f = self.get_func()
294+
class ThreadTest(threading.Thread):
295+
def __init__(self, f, test):
296+
super().__init__()
297+
self.f = f
298+
self.test = test
299+
def run(self):
300+
del self.f
301+
self.test.assertEqual(LAST_FREED, 500)
302+
303+
SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(500))
304+
tt = ThreadTest(f, self)
305+
del f
306+
tt.start()
307+
tt.join()
308+
self.assertEqual(LAST_FREED, 500)
309+
215310
def test_main(verbose=None):
216311
from test import test_code
217312
run_doctest(test_code, verbose)
218-
run_unittest(CodeTest, CodeConstsTest, CodeWeakRefTest)
219-
313+
tests = [CodeTest, CodeConstsTest, CodeWeakRefTest]
314+
if check_impl_detail(cpython=True):
315+
tests.append(CoExtra)
316+
run_unittest(*tests)
220317

221318
if __name__ == "__main__":
222319
test_main()

‎Misc/NEWS

Copy file name to clipboardExpand all lines: Misc/NEWS
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.7.0 alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- bpo-30604: Move co_extra_freefuncs from per-thread to per-interpreter to
14+
avoid crashes.
15+
1316
- bpo-30597: ``print`` now shows expected input in custom error message when
1417
used as a Python 2 statement. Patch by Sanyam Khurana.
1518

‎Objects/codeobject.c

Copy file name to clipboardExpand all lines: Objects/codeobject.c
+18-12Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -416,11 +416,11 @@ static void
416416
code_dealloc(PyCodeObject *co)
417417
{
418418
if (co->co_extra != NULL) {
419-
PyThreadState *tstate = PyThreadState_Get();
419+
PyInterpreterState *interp = PyThreadState_Get()->interp;
420420
_PyCodeObjectExtra *co_extra = co->co_extra;
421421

422422
for (Py_ssize_t i = 0; i < co_extra->ce_size; i++) {
423-
freefunc free_extra = tstate->co_extra_freefuncs[i];
423+
freefunc free_extra = interp->co_extra_freefuncs[i];
424424

425425
if (free_extra != NULL) {
426426
free_extra(co_extra->ce_extras[i]);
@@ -830,8 +830,6 @@ _PyCode_CheckLineNumber(PyCodeObject* co, int lasti, PyAddrPair *bounds)
830830
int
831831
_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
832832
{
833-
assert(*extra == NULL);
834-
835833
if (!PyCode_Check(code)) {
836834
PyErr_BadInternalCall();
837835
return -1;
@@ -840,8 +838,8 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
840838
PyCodeObject *o = (PyCodeObject*) code;
841839
_PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra*) o->co_extra;
842840

843-
844841
if (co_extra == NULL || co_extra->ce_size <= index) {
842+
*extra = NULL;
845843
return 0;
846844
}
847845

@@ -853,10 +851,10 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
853851
int
854852
_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
855853
{
856-
PyThreadState *tstate = PyThreadState_Get();
854+
PyInterpreterState *interp = PyThreadState_Get()->interp;
857855

858856
if (!PyCode_Check(code) || index < 0 ||
859-
index >= tstate->co_extra_user_count) {
857+
index >= interp->co_extra_user_count) {
860858
PyErr_BadInternalCall();
861859
return -1;
862860
}
@@ -871,13 +869,13 @@ _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
871869
}
872870

873871
co_extra->ce_extras = PyMem_Malloc(
874-
tstate->co_extra_user_count * sizeof(void*));
872+
interp->co_extra_user_count * sizeof(void*));
875873
if (co_extra->ce_extras == NULL) {
876874
PyMem_Free(co_extra);
877875
return -1;
878876
}
879877

880-
co_extra->ce_size = tstate->co_extra_user_count;
878+
co_extra->ce_size = interp->co_extra_user_count;
881879

882880
for (Py_ssize_t i = 0; i < co_extra->ce_size; i++) {
883881
co_extra->ce_extras[i] = NULL;
@@ -887,20 +885,28 @@ _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
887885
}
888886
else if (co_extra->ce_size <= index) {
889887
void** ce_extras = PyMem_Realloc(
890-
co_extra->ce_extras, tstate->co_extra_user_count * sizeof(void*));
888+
co_extra->ce_extras, interp->co_extra_user_count * sizeof(void*));
891889

892890
if (ce_extras == NULL) {
893891
return -1;
894892
}
895893

896894
for (Py_ssize_t i = co_extra->ce_size;
897-
i < tstate->co_extra_user_count;
895+
i < interp->co_extra_user_count;
898896
i++) {
899897
ce_extras[i] = NULL;
900898
}
901899

902900
co_extra->ce_extras = ce_extras;
903-
co_extra->ce_size = tstate->co_extra_user_count;
901+
co_extra->ce_size = interp->co_extra_user_count;
902+
}
903+
904+
if (co_extra->ce_extras[index] != NULL) {
905+
freefunc free = interp->co_extra_freefuncs[index];
906+
907+
if (free != NULL) {
908+
free(co_extra->ce_extras[index]);
909+
}
904910
}
905911

906912
co_extra->ce_extras[index] = extra;

‎Python/ceval.c

Copy file name to clipboardExpand all lines: Python/ceval.c
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5287,14 +5287,14 @@ _Py_GetDXProfile(PyObject *self, PyObject *args)
52875287
Py_ssize_t
52885288
_PyEval_RequestCodeExtraIndex(freefunc free)
52895289
{
5290-
PyThreadState *tstate = PyThreadState_Get();
5290+
PyInterpreterState *interp = PyThreadState_Get()->interp;
52915291
Py_ssize_t new_index;
52925292

5293-
if (tstate->co_extra_user_count == MAX_CO_EXTRA_USERS - 1) {
5293+
if (interp->co_extra_user_count == MAX_CO_EXTRA_USERS - 1) {
52945294
return -1;
52955295
}
5296-
new_index = tstate->co_extra_user_count++;
5297-
tstate->co_extra_freefuncs[new_index] = free;
5296+
new_index = interp->co_extra_user_count++;
5297+
interp->co_extra_freefuncs[new_index] = free;
52985298
return new_index;
52995299
}
53005300

‎Python/pystate.c

Copy file name to clipboardExpand all lines: Python/pystate.c
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ PyInterpreterState_New(void)
111111
interp->importlib = NULL;
112112
interp->import_func = NULL;
113113
interp->eval_frame = _PyEval_EvalFrameDefault;
114+
interp->co_extra_user_count = 0;
114115
#ifdef HAVE_DLOPEN
115116
#if HAVE_DECL_RTLD_NOW
116117
interp->dlopenflags = RTLD_NOW;
@@ -281,7 +282,6 @@ new_threadstate(PyInterpreterState *interp, int init)
281282

282283
tstate->coroutine_wrapper = NULL;
283284
tstate->in_coroutine_wrapper = 0;
284-
tstate->co_extra_user_count = 0;
285285

286286
tstate->async_gen_firstiter = NULL;
287287
tstate->async_gen_finalizer = NULL;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.