gh-84570: Add Threading Timeout Helpers to Internal C-API by ericsnowcurrently · Pull Request #110948 · python/cpython
#include "Python.h" #include "pycore_ceval.h" // _PyEval_MakePendingCalls() #include "pycore_dict.h" // _PyDict_Pop() #include "pycore_interp.h" // _PyInterpreterState.threads.count #include "pycore_moduleobject.h" // _PyModule_GetState()
/* Helper to acquire an interruptible lock with a timeout. If the lock acquire * is interrupted, signal handlers are run, and if they raise an exception, * PY_LOCK_INTR is returned. Otherwise, PY_LOCK_ACQUIRED or PY_LOCK_FAILURE * are returned, depending on whether the lock can be acquired within the * timeout. */ static PyLockStatus static inline PyLockStatus acquire_timed(PyThread_type_lock lock, _PyTime_t timeout) { PyThreadState *tstate = _PyThreadState_GET(); _PyTime_t endtime = 0; if (timeout > 0) { endtime = _PyDeadline_Init(timeout); }
PyLockStatus r; do { _PyTime_t microseconds; microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING);
/* first a simple non-blocking try without releasing the GIL */ r = PyThread_acquire_lock_timed(lock, 0, 0); if (r == PY_LOCK_FAILURE && microseconds != 0) { Py_BEGIN_ALLOW_THREADS r = PyThread_acquire_lock_timed(lock, microseconds, 1); Py_END_ALLOW_THREADS }
if (r == PY_LOCK_INTR) { /* Run signal handlers if we were interrupted. Propagate * exceptions from signal handlers, such as KeyboardInterrupt, by * passing up PY_LOCK_INTR. */ if (_PyEval_MakePendingCalls(tstate) < 0) { return PY_LOCK_INTR; }
/* If we're using a timeout, recompute the timeout after processing * signals, since those can take time. */ if (timeout > 0) { timeout = _PyDeadline_Get(endtime);
/* Check for negative values, since those mean block forever. */ if (timeout < 0) { r = PY_LOCK_FAILURE; } } } } while (r == PY_LOCK_INTR); /* Retry if we were interrupted. */
return r; return PyThread_acquire_lock_timed_with_retries(lock, timeout); }
static int
*timeout = unset_timeout ;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pO:acquire", kwlist, &blocking, &timeout_obj)) return -1;
// XXX Use PyThread_ParseTimeoutArg().
const _PyTime_t unset_timeout = _PyTime_FromSeconds(-1); *timeout = unset_timeout;
if (timeout_obj && _PyTime_FromSecondsObject(timeout, timeout_obj, _PyTime_ROUND_TIMEOUT) < 0)