[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()

`