[3.6] bpo-30604: Move co_extra_freefuncs to interpreter state to avoi… · python/cpython@2997fec (original) (raw)
`@@ -103,9 +103,11 @@
`
103
103
`"""
`
104
104
``
105
105
`import sys
`
``
106
`+
import threading
`
106
107
`import unittest
`
107
108
`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)
`
109
111
``
110
112
``
111
113
`def consts(t):
`
`@@ -212,11 +214,106 @@ def callback(code):
`
212
214
`self.assertTrue(self.called)
`
213
215
``
214
216
``
``
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
+
215
310
`def test_main(verbose=None):
`
216
311
`from test import test_code
`
217
312
`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)
`
220
317
``
221
318
`if name == "main":
`
222
319
`test_main()
`