bpo-39877: Fix PyEval_RestoreThread() for daemon threads (GH-18811) · python/cpython@eb4e2ae (original) (raw)

`@@ -188,6 +188,25 @@ static size_t opcache_global_misses = 0;

`

188

188

`#include "pythread.h"

`

189

189

`#include "ceval_gil.h"

`

190

190

``

``

191

`+

static void

`

``

192

`+

ensure_tstate_not_null(const char *func, PyThreadState *tstate)

`

``

193

`+

{

`

``

194

`+

if (tstate == NULL) {

`

``

195

`+

_Py_FatalErrorFunc(func, "current thread state is NULL");

`

``

196

`+

}

`

``

197

`+

}

`

``

198

+

``

199

+

``

200

`+

#ifndef NDEBUG

`

``

201

`+

static int is_tstate_valid(PyThreadState *tstate)

`

``

202

`+

{

`

``

203

`+

assert(!_PyMem_IsPtrFreed(tstate));

`

``

204

`+

assert(!_PyMem_IsPtrFreed(tstate->interp));

`

``

205

`+

return 1;

`

``

206

`+

}

`

``

207

`+

#endif

`

``

208

+

``

209

+

191

210

`int

`

192

211

`PyEval_ThreadsInitialized(void)

`

193

212

`{

`

`@@ -208,6 +227,7 @@ PyEval_InitThreads(void)

`

208

227

`PyThread_init_thread();

`

209

228

`create_gil(gil);

`

210

229

`PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);

`

``

230

`+

ensure_tstate_not_null(func, tstate);

`

211

231

`take_gil(ceval, tstate);

`

212

232

``

213

233

`struct _pending_calls *pending = &ceval->pending;

`

`@@ -235,14 +255,26 @@ _PyEval_FiniThreads(struct _ceval_runtime_state *ceval)

`

235

255

` }

`

236

256

`}

`

237

257

``

``

258

`+

/* This function is designed to exit daemon threads immediately rather than

`

``

259

`+

taking the GIL if Py_Finalize() has been called.

`

``

260

+

``

261

`+

The caller must not hold the GIL, since this function does not release

`

``

262

`+

the GIL before exiting the thread.

`

``

263

+

``

264

`+

When this function is called by a daemon thread after Py_Finalize() has been

`

``

265

`+

called, the GIL does no longer exist.

`

``

266

+

``

267

`+

tstate must be non-NULL. */

`

238

268

`static inline void

`

239

269

`exit_thread_if_finalizing(PyThreadState *tstate)

`

240

270

`{

`

241

``

`-

_PyRuntimeState *runtime = tstate->interp->runtime;

`

242

``

`-

/* _Py_Finalizing is protected by the GIL */

`

``

271

`+

/* bpo-39877: Access _PyRuntime directly rather than using

`

``

272

`+

tstate->interp->runtime to support calls from Python daemon threads.

`

``

273

`+

After Py_Finalize() has been called, tstate can be a dangling pointer:

`

``

274

`+

point to PyThreadState freed memory. */

`

``

275

`+

_PyRuntimeState *runtime = &_PyRuntime;

`

243

276

`PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);

`

244

277

`if (finalizing != NULL && finalizing != tstate) {

`

245

``

`-

drop_gil(&runtime->ceval, tstate);

`

246

278

`PyThread_exit_thread();

`

247

279

` }

`

248

280

`}

`

`@@ -280,13 +312,14 @@ void

`

280

312

`PyEval_AcquireLock(void)

`

281

313

`{

`

282

314

`_PyRuntimeState *runtime = &_PyRuntime;

`

283

``

`-

struct _ceval_runtime_state *ceval = &runtime->ceval;

`

284

315

`PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);

`

285

``

`-

if (tstate == NULL) {

`

286

``

`-

Py_FatalError("current thread state is NULL");

`

287

``

`-

}

`

288

``

`-

take_gil(ceval, tstate);

`

``

316

`+

ensure_tstate_not_null(func, tstate);

`

``

317

+

289

318

`exit_thread_if_finalizing(tstate);

`

``

319

`+

assert(is_tstate_valid(tstate));

`

``

320

+

``

321

`+

struct _ceval_runtime_state *ceval = &runtime->ceval;

`

``

322

`+

take_gil(ceval, tstate);

`

290

323

`}

`

291

324

``

292

325

`void

`

`@@ -304,15 +337,18 @@ PyEval_ReleaseLock(void)

`

304

337

`void

`

305

338

`PyEval_AcquireThread(PyThreadState *tstate)

`

306

339

`{

`

307

``

`-

assert(tstate != NULL);

`

``

340

`+

ensure_tstate_not_null(func, tstate);

`

``

341

+

``

342

`+

exit_thread_if_finalizing(tstate);

`

``

343

`+

assert(is_tstate_valid(tstate));

`

308

344

``

309

345

`_PyRuntimeState *runtime = tstate->interp->runtime;

`

310

346

`struct _ceval_runtime_state *ceval = &runtime->ceval;

`

311

347

``

312

348

`/* Check someone has called PyEval_InitThreads() to create the lock */

`

313

349

`assert(gil_created(&ceval->gil));

`

``

350

+

314

351

`take_gil(ceval, tstate);

`

315

``

`-

exit_thread_if_finalizing(tstate);

`

316

352

`if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {

`

317

353

`Py_FatalError("non-NULL old thread state");

`

318

354

` }

`

`@@ -344,8 +380,9 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)

`

344

380

`return;

`

345

381

` }

`

346

382

`recreate_gil(&ceval->gil);

`

347

``

`-

PyThreadState *current_tstate = _PyRuntimeState_GetThreadState(runtime);

`

348

``

`-

take_gil(ceval, current_tstate);

`

``

383

`+

PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);

`

``

384

`+

ensure_tstate_not_null(func, tstate);

`

``

385

`+

take_gil(ceval, tstate);

`

349

386

``

350

387

`struct _pending_calls *pending = &ceval->pending;

`

351

388

`pending->lock = PyThread_allocate_lock();

`

`@@ -354,7 +391,7 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)

`

354

391

` }

`

355

392

``

356

393

`/* Destroy all threads except the current one */

`

357

``

`-

_PyThreadState_DeleteExcept(runtime, current_tstate);

`

``

394

`+

_PyThreadState_DeleteExcept(runtime, tstate);

`

358

395

`}

`

359

396

``

360

397

`/* This function is used to signal that async exceptions are waiting to be

`

`@@ -383,16 +420,16 @@ PyEval_SaveThread(void)

`

383

420

`void

`

384

421

`PyEval_RestoreThread(PyThreadState *tstate)

`

385

422

`{

`

386

``

`-

assert(tstate != NULL);

`

``

423

`+

ensure_tstate_not_null(func, tstate);

`

``

424

+

``

425

`+

exit_thread_if_finalizing(tstate);

`

``

426

`+

assert(is_tstate_valid(tstate));

`

387

427

``

388

428

`_PyRuntimeState *runtime = tstate->interp->runtime;

`

389

429

`struct _ceval_runtime_state *ceval = &runtime->ceval;

`

390

430

`assert(gil_created(&ceval->gil));

`

391

431

``

392

``

`-

int err = errno;

`

393

432

`take_gil(ceval, tstate);

`

394

``

`-

exit_thread_if_finalizing(tstate);

`

395

``

`-

errno = err;

`

396

433

``

397

434

`_PyThreadState_Swap(&runtime->gilstate, tstate);

`

398

435

`}

`

`@@ -750,11 +787,14 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)

`

750

787

`PyObject **fastlocals, **freevars;

`

751

788

`PyObject retval = NULL; / Return value */

`

752

789

`_PyRuntimeState * const runtime = &_PyRuntime;

`

753

``

`-

PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);

`

754

790

`struct _ceval_runtime_state * const ceval = &runtime->ceval;

`

755

791

`_Py_atomic_int * const eval_breaker = &ceval->eval_breaker;

`

756

792

`PyCodeObject *co;

`

757

793

``

``

794

`+

PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);

`

``

795

`+

ensure_tstate_not_null(func, tstate);

`

``

796

`+

assert(is_tstate_valid(tstate));

`

``

797

+

758

798

`/* when tracing we set things up so that

`

759

799

``

760

800

` not (instr_lb <= current_bytecode_offset < instr_ub)

`

`@@ -1242,11 +1282,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)

`

1242

1282

``

1243

1283

`/* Other threads may run now */

`

1244

1284

``

1245

``

`-

take_gil(ceval, tstate);

`

1246

``

-

1247

1285

`/* Check if we should make a quick exit. */

`

1248

1286

`exit_thread_if_finalizing(tstate);

`

1249

1287

``

``

1288

`+

take_gil(ceval, tstate);

`

``

1289

+

1250

1290

`if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {

`

1251

1291

`Py_FatalError("orphan tstate");

`

1252

1292

` }

`