diff --git a/python/ray/_raylet.pxd b/python/ray/_raylet.pxd index 98b445fc3abb..d87de681a360 100644 --- a/python/ray/_raylet.pxd +++ b/python/ray/_raylet.pxd @@ -72,6 +72,7 @@ cdef extern from "Python.h": ctypedef struct CPyThreadState "PyThreadState": int recursion_limit int recursion_remaining + int c_recursion_remaining # From Include/ceveal.h#67 int Py_GetRecursionLimit() diff --git a/python/ray/_raylet.pyx b/python/ray/_raylet.pyx index b63cd91c0526..c40feb4f4f58 100644 --- a/python/ray/_raylet.pyx +++ b/python/ray/_raylet.pyx @@ -679,29 +679,63 @@ def compute_task_id(ObjectRef object_ref): cdef increase_recursion_limit(): - """Double the recusion limit if current depth is close to the limit""" + """ + Ray does some weird things with asio fibers and asyncio to run asyncio actors. + This results in the Python interpreter thinking there's a lot of recursion depth, + so we need to increase the limit when we start getting close. + + 0x30C0000 is Python 3.12 + On 3.12, when recursion depth increases, c_recursion_remaining will decrease, + and that's what's actually compared to raise a RecursionError. So increasing + it by 1000 when it drops below 1000 will keep us from raising the RecursionError. + https://github.com/python/cpython/blob/bfb9e2f4a4e690099ec2ec53c08b90f4d64fde36/Python/pystate.c#L1353 + 0x30B00A4 is Python 3.11 + On 3.11, the recursion depth can be calculated with recursion_limit - recursion_remaining. + We can get the current limit with Py_GetRecursionLimit and set it with Py_SetRecursionLimit. + We'll double the limit when there's less than 500 remaining. + On older versions + There's simply a recursion_depth variable and we'll increase the max the same + way we do for 3.11. + """ cdef: - CPyThreadState * s = PyThreadState_Get() - int current_limit = Py_GetRecursionLimit() - int new_limit = current_limit * 2 cdef extern from *: """ #if PY_VERSION_HEX >= 0x30C0000 - #define CURRENT_DEPTH(x) ((x)->py_recursion_limit - (x)->py_recursion_remaining) + bool IncreaseRecursionLimitIfNeeded(PyThreadState *x) { + if (x->c_recursion_remaining < 1000) { + x->c_recursion_remaining += 1000; + return true; + } + return false; + } #elif PY_VERSION_HEX >= 0x30B00A4 - #define CURRENT_DEPTH(x) ((x)->recursion_limit - (x)->recursion_remaining) + bool IncreaseRecursionLimitIfNeeded(PyThreadState *x) { + int current_limit = Py_GetRecursionLimit(); + int current_depth = x->recursion_limit - x->recursion_remaining; + if (current_limit - current_depth < 500) { + Py_SetRecursionLimit(current_limit * 2); + return true; + } + return false; + } #else - #define CURRENT_DEPTH(x) ((x)->recursion_depth) + bool IncreaseRecursionLimitIfNeeded(PyThreadState *x) { + int current_limit = Py_GetRecursionLimit(); + if (current_limit - x->recursion_depth < 500) { + Py_SetRecursionLimit(current_limit * 2); + return true; + } + return false; + } #endif """ - int CURRENT_DEPTH(CPyThreadState *x) - - int current_depth = CURRENT_DEPTH(s) - if current_limit - current_depth < 500: - Py_SetRecursionLimit(new_limit) - logger.debug("Increasing Python recursion limit to {} " - "current recursion depth is {}.".format( - new_limit, current_depth)) + c_bool IncreaseRecursionLimitIfNeeded(CPyThreadState *x) + + CPyThreadState * s = PyThreadState_Get() + c_bool increased_recursion_limit = IncreaseRecursionLimitIfNeeded(s) + + if increased_recursion_limit: + logger.debug("Increased Python recursion limit") cdef CObjectLocationPtrToDict(CObjectLocation* c_object_location):