diff --git a/ortools/linear_solver/python/model_builder_helper.cc b/ortools/linear_solver/python/model_builder_helper.cc index 326e52c2e8..5d361c3246 100644 --- a/ortools/linear_solver/python/model_builder_helper.cc +++ b/ortools/linear_solver/python/model_builder_helper.cc @@ -310,7 +310,7 @@ std::shared_ptr WeightedSumArguments( } #if PY_VERSION_HEX >= 0x030E00A7 && !defined(PYPY_VERSION) -bool check_unique_temporary(PyObject* op) { +bool was_optimized_in_function_call(PyObject* op) { PyFrameObject* frame = PyEval_GetFrame(); if (frame == NULL) { return false; @@ -330,12 +330,24 @@ bool check_unique_temporary(PyObject* op) { return false; } +bool IsOnwedExclusivelyThroughPyBind11(PyObject* op) { +#if !defined(Py_GIL_DISABLED) + return Py_REFCNT(ob) == 3; +#else + // NOTE: the entire ob_ref_shared field must be zero, including flags, to + // ensure that other threads cannot concurrently create new references to + // this object. + return (_Py_IsOwnedByCurrentThread(ob) && + _Py_atomic_load_uint32_relaxed(&ob->ob_ref_local) == 3 && + _Py_atomic_load_ssize_relaxed(&ob->ob_ref_shared) == 0); +#endif +} + template bool IsFree(std::shared_ptr expr) { - PyObject* lhs = py::cast(expr).ptr(); - const int num_uses = Py_REFCNT(lhs); - const bool is_referenced_in_caller_frame = check_unique_temporary(lhs); - return num_uses == 3 && !is_referenced_in_caller_frame; + PyObject* op = py::cast(expr).ptr(); + return IsOnwedExclusivelyThroughPyBind11(op) && + !was_optimized_in_function_call(op); } #else template diff --git a/ortools/sat/python/cp_model_helper.cc b/ortools/sat/python/cp_model_helper.cc index e21a33b602..0661ba717e 100644 --- a/ortools/sat/python/cp_model_helper.cc +++ b/ortools/sat/python/cp_model_helper.cc @@ -1115,7 +1115,7 @@ std::shared_ptr CpBaseModel::AddRoutesInternal( } #if PY_VERSION_HEX >= 0x030E00A7 && !defined(PYPY_VERSION) -bool check_unique_temporary(PyObject* op) { +bool was_optimized_in_function_call(PyObject* op) { PyFrameObject* frame = PyEval_GetFrame(); if (frame == NULL) { return false; @@ -1135,12 +1135,24 @@ bool check_unique_temporary(PyObject* op) { return false; } +bool IsOnwedExclusivelyThroughPyBind11(PyObject* op) { +#if !defined(Py_GIL_DISABLED) + return Py_REFCNT(ob) == 3; +#else + // NOTE: the entire ob_ref_shared field must be zero, including flags, to + // ensure that other threads cannot concurrently create new references to + // this object. + return (_Py_IsOwnedByCurrentThread(ob) && + _Py_atomic_load_uint32_relaxed(&ob->ob_ref_local) == 3 && + _Py_atomic_load_ssize_relaxed(&ob->ob_ref_shared) == 0); +#endif +} + template bool IsFree(std::shared_ptr expr) { - PyObject* lhs = py::cast(expr).ptr(); - const int num_uses = Py_REFCNT(lhs); - const bool is_referenced_in_caller_frame = check_unique_temporary(lhs); - return num_uses == 3 && !is_referenced_in_caller_frame; + PyObject* op = py::cast(expr).ptr(); + return IsOnwedExclusivelyThroughPyBind11(op) && + !was_optimized_in_function_call(op); } #else template