Destroying subinterpreters on a embbeded context with CPython 3.9

Hello there,

We have an application that has support for multiple subinterpreters that execute logic isolated from each other. They only have access to an API to call into the application. Each subinterpreter is created using _tstate = Py_NewInterpreter, we then import a module and call a func inside it.

When it’s time to destroy the subinterpreters, we do something like this:

...
PyEval_AcquireThread(_tstate);
if (EndInterpreterSafe(_tstate))
	PyEval_ReleaseLock();
else
	PyEval_ReleaseThread(tstate);
...

EndInterpreterSafe returns true when Py_EndInterpreter runs, which only happens if all of the criteria on its implementation are met (given thread is the current one, there’s no frame and it is the last thread).

Despite using a deprecated method, this works fine with 3.7. We were attempting a migration to 3.9, however, the code above crashes on PyEval_ReleaseLock after ending the subinterpreter because the current thread will be invalid by that time. We need to run something that releases the lock since it is still held after Py_EndInterpreter returns.

It’s fair to say that I’m new to using the python c-api. Been reading the documentation and issues about the topic and noticed that it is an area of active development. However, I’m still failing to connect the dots or grasp what are the relevant changes from 3.7 → 3.9 and what’s the correct way to destroy a subinterpreter in this scenario.

Any help is welcome, thanks in advance.

For anyone facing the same issue, found a solution.
The Py_EvalReleaseLock() call was replaced with:

PyThreadState_Swap(PyInterpreterState_ThreadHead(PyInterpreterState_Main()));
PyEval_SaveThread();

This swaps the current thread state to the main interpreter thread, and then we can safely reset the current state and release the lock.