Cleanup: move Python's GIL ensure/release to the function start/end

Avoid errors accessing state without the GIL (#127767) by moving
GIL ensure/release to the star/end of the function body.
This commit is contained in:
Campbell Barton
2025-02-13 17:56:42 +11:00
parent 886e40f136
commit e52f85b33c
10 changed files with 40 additions and 62 deletions

View File

@@ -292,11 +292,9 @@ PyObject *BPY_app_handlers_struct()
void BPY_app_handlers_reset(const bool do_all)
{
PyGILState_STATE gilstate;
PyGILState_STATE gilstate = PyGILState_Ensure();
int pos = 0;
gilstate = PyGILState_Ensure();
if (do_all) {
for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
/* clear list */

View File

@@ -44,10 +44,9 @@ static double handle_returned_value(PyObject *function, PyObject *ret)
static double py_timer_execute(uintptr_t /*uuid*/, void *user_data)
{
PyObject *function = static_cast<PyObject *>(user_data);
PyGILState_STATE gilstate = PyGILState_Ensure();
PyGILState_STATE gilstate;
gilstate = PyGILState_Ensure();
PyObject *function = static_cast<PyObject *>(user_data);
PyObject *py_ret = PyObject_CallObject(function, nullptr);
const double ret = handle_returned_value(function, py_ret);
@@ -59,11 +58,9 @@ static double py_timer_execute(uintptr_t /*uuid*/, void *user_data)
static void py_timer_free(uintptr_t /*uuid*/, void *user_data)
{
PyGILState_STATE gilstate = PyGILState_Ensure();
PyObject *function = static_cast<PyObject *>(user_data);
PyGILState_STATE gilstate;
gilstate = PyGILState_Ensure();
Py_DECREF(function);
PyGILState_Release(gilstate);

View File

@@ -269,14 +269,12 @@ std::optional<StringRefNull> BPY_app_translations_py_pgettext(const StringRef ms
tmp = BLT_lang_get();
if (!STREQ(tmp, locale) || !get_translations_cache()) {
PyGILState_STATE _py_state;
/* This function may be called from C (i.e. outside of python interpreter 'context'). */
PyGILState_STATE _py_state = PyGILState_Ensure();
STRNCPY(locale, tmp);
/* Locale changed or cache does not exist, refresh the whole cache! */
/* This func may be called from C (i.e. outside of python interpreter 'context'). */
_py_state = PyGILState_Ensure();
_build_translations_cache(_translations->py_messages, locale);
PyGILState_Release(_py_state);

View File

@@ -62,10 +62,11 @@ static int bpy_cli_command_exec(bContext *C,
const int argc,
const char **argv)
{
int exit_code = EXIT_FAILURE;
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
int exit_code = EXIT_FAILURE;
/* For the most part `sys.argv[-argc:]` is sufficient & less trouble than re-creating this
* list. Don't do this because:
* - Python scripts *could* have manipulated `sys.argv` (although it's bad practice).

View File

@@ -265,7 +265,6 @@ void BPY_driver_reset()
{
PyGILState_STATE gilstate;
const bool use_gil = true; /* !PyC_IsInterpreterActive(); */
if (use_gil) {
gilstate = PyGILState_Ensure();
}

View File

@@ -172,7 +172,6 @@ void BPY_context_dict_clear_members_array(void **dict_p,
{
PyGILState_STATE gilstate;
const bool use_gil = !PyC_IsInterpreterActive();
if (use_gil) {
gilstate = PyGILState_Ensure();
}
@@ -565,10 +564,8 @@ void BPY_python_end(const bool do_python_exit)
BLI_assert_msg(Py_IsInitialized() != 0, "Python must be initialized");
#endif
PyGILState_STATE gilstate;
/* Finalizing, no need to grab the state, except when we are a module. */
gilstate = PyGILState_Ensure();
PyGILState_STATE gilstate = PyGILState_Ensure();
/* Frees the Python-driver name-space & cached data. */
BPY_driver_exit();
@@ -735,16 +732,15 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *
{
PyGILState_STATE gilstate;
const bool use_gil = !PyC_IsInterpreterActive();
if (use_gil) {
gilstate = PyGILState_Ensure();
}
PyObject *pyctx;
PyObject *item;
PointerRNA *ptr = nullptr;
bool done = false;
if (use_gil) {
gilstate = PyGILState_Ensure();
}
pyctx = (PyObject *)CTX_py_dict_get(C);
item = PyDict_GetItemString(pyctx, member);

View File

@@ -130,22 +130,21 @@ static PyObject *python_compat_wrapper_PyRun_FileExFlags(FILE *fp,
static bool python_script_exec(
bContext *C, const char *filepath, Text *text, ReportList *reports, const bool do_jump)
{
Main *bmain_old = CTX_data_main(C);
PyObject *py_dict = nullptr, *py_result = nullptr;
PyGILState_STATE gilstate;
char filepath_dummy[FILE_MAX];
/** The `__file__` added into the name-space. */
const char *filepath_namespace = nullptr;
BLI_assert(filepath || text);
if (filepath == nullptr && text == nullptr) {
return false;
}
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
Main *bmain_old = CTX_data_main(C);
PyObject *py_dict = nullptr, *py_result = nullptr;
char filepath_dummy[FILE_MAX];
/** The `__file__` added into the name-space. */
const char *filepath_namespace = nullptr;
PyObject *main_mod = PyC_MainModule_Backup();
if (text) {
@@ -360,7 +359,6 @@ bool BPY_run_string_as_number(bContext *C,
BPy_RunErrInfo *err_info,
double *r_value)
{
PyGILState_STATE gilstate;
bool ok = true;
if (expr[0] == '\0') {
@@ -368,6 +366,7 @@ bool BPY_run_string_as_number(bContext *C,
return ok;
}
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
@@ -388,7 +387,6 @@ bool BPY_run_string_as_string_and_len(bContext *C,
char **r_value,
size_t *r_value_len)
{
PyGILState_STATE gilstate;
bool ok = true;
if (expr[0] == '\0') {
@@ -396,6 +394,7 @@ bool BPY_run_string_as_string_and_len(bContext *C,
return ok;
}
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_len);
@@ -423,7 +422,6 @@ bool BPY_run_string_as_string_and_len_or_none(bContext *C,
char **r_value,
size_t *r_value_len)
{
PyGILState_STATE gilstate;
bool ok = true;
if (expr[0] == '\0') {
@@ -431,6 +429,7 @@ bool BPY_run_string_as_string_and_len_or_none(bContext *C,
return ok;
}
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
ok = PyC_RunString_AsStringAndSizeOrNone(
@@ -459,7 +458,6 @@ bool BPY_run_string_as_intptr(bContext *C,
BPy_RunErrInfo *err_info,
intptr_t *r_value)
{
PyGILState_STATE gilstate;
bool ok = true;
if (expr[0] == '\0') {
@@ -467,6 +465,7 @@ bool BPY_run_string_as_intptr(bContext *C,
return ok;
}
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);

View File

@@ -9407,12 +9407,12 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
C = BPY_context_get();
}
bpy_context_set(C, &gilstate);
/* Annoying! We need to check if the screen gets set to nullptr which is a
* hint that the file was actually re-loaded. */
const bool is_valid_wm = (CTX_wm_manager(C) != nullptr);
bpy_context_set(C, &gilstate);
if (!(is_staticmethod || is_classmethod)) {
/* Some data-types (operator, render engine) can store PyObjects for re-use. */
if (ptr->data) {
@@ -9704,10 +9704,9 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
static void bpy_class_free(void *pyob_ptr)
{
PyObject *self = (PyObject *)pyob_ptr;
PyGILState_STATE gilstate;
PyGILState_STATE gilstate = PyGILState_Ensure();
gilstate = PyGILState_Ensure();
PyObject *self = (PyObject *)pyob_ptr;
/* Breaks re-registering classes. */
// PyDict_Clear(((PyTypeObject *)self)->tp_dict);
@@ -9821,12 +9820,10 @@ void pyrna_alloc_types()
* or any errors in "bpy_types.py" at load time, so errors don't go unnoticed. */
#ifndef NDEBUG
PyGILState_STATE gilstate;
PyGILState_STATE gilstate = PyGILState_Ensure();
PropertyRNA *prop;
gilstate = PyGILState_Ensure();
/* Avoid doing this lookup for every getattr. */
PointerRNA ptr = RNA_blender_rna_pointer_create();
prop = RNA_struct_find_property(&ptr, "structs");

View File

@@ -46,11 +46,11 @@ static const EnumPropertyItem region_draw_mode_items[] = {
static void cb_region_draw(const bContext *C, ARegion * /*region*/, void *customdata)
{
PyObject *cb_func, *cb_args, *result;
PyGILState_STATE gilstate;
bpy_context_set((bContext *)C, &gilstate);
PyObject *cb_func, *cb_args, *result;
cb_func = PyTuple_GET_ITEM((PyObject *)customdata, 1);
cb_args = PyTuple_GET_ITEM((PyObject *)customdata, 2);
result = PyObject_CallObject(cb_func, cb_args);
@@ -82,11 +82,10 @@ static PyObject *PyC_Tuple_CopySized(PyObject *src, int len_dst)
static void cb_wm_cursor_draw(bContext *C, int x, int y, void *customdata)
{
PyObject *cb_func, *cb_args, *result;
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
PyObject *cb_func, *cb_args, *result;
cb_func = PyTuple_GET_ITEM((PyObject *)customdata, 1);
cb_args = PyTuple_GET_ITEM((PyObject *)customdata, 2);
@@ -471,19 +470,12 @@ PyObject *pyrna_callback_classmethod_remove(PyObject * /*self*/, PyObject *args)
static void cb_customdata_free(void *customdata)
{
PyGILState_STATE gilstate = PyGILState_Ensure();
PyObject *tuple = static_cast<PyObject *>(customdata);
bool use_gil = true; /* !PyC_IsInterpreterActive(); */
PyGILState_STATE gilstate;
if (use_gil) {
gilstate = PyGILState_Ensure();
}
Py_DECREF(tuple);
if (use_gil) {
PyGILState_Release(gilstate);
}
PyGILState_Release(gilstate);
}
void BPY_callback_screen_free(ARegionType *art)

View File

@@ -248,11 +248,11 @@ static void py_rna_gizmo_handler_range_get_cb(const wmGizmo * /*gz*/,
wmGizmoProperty *gz_prop,
void *value_p)
{
const PyGILState_STATE gilstate = PyGILState_Ensure();
BPyGizmoHandlerUserData *data = static_cast<BPyGizmoHandlerUserData *>(
gz_prop->custom_func.user_data);
const PyGILState_STATE gilstate = PyGILState_Ensure();
PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET], nullptr);
if (ret == nullptr) {
goto fail;
@@ -302,10 +302,11 @@ fail:
static void py_rna_gizmo_handler_free_cb(const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop)
{
const PyGILState_STATE gilstate = PyGILState_Ensure();
BPyGizmoHandlerUserData *data = static_cast<BPyGizmoHandlerUserData *>(
gz_prop->custom_func.user_data);
const PyGILState_STATE gilstate = PyGILState_Ensure();
for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
Py_XDECREF(data->fn_slots[i]);
}