Skip to content
Next Next commit
Factor out PyThread_ParseTimeoutArg().
  • Loading branch information
ericsnowcurrently committed Oct 17, 2023
commit 51b80c72251052ca49588c4bca6c1f5b7c0035c7
6 changes: 6 additions & 0 deletions Include/internal/pycore_pythread.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ extern int _PyThread_at_fork_reinit(PyThread_type_lock *lock);
// unset: -1 seconds, in nanoseconds
#define PyThread_UNSET_TIMEOUT ((_PyTime_t)(-1 * 1000 * 1000 * 1000))

// Exported for the _xxinterpchannels module.
PyAPI_FUNC(int) PyThread_ParseTimeoutArg(
PyObject *arg,
int blocking,
PY_TIMEOUT_T *timeout);

/* 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
Expand Down
2 changes: 2 additions & 0 deletions Modules/_queuemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
PY_TIMEOUT_T microseconds;
PyThreadState *tstate = PyThreadState_Get();

// XXX Use PyThread_ParseTimeoutArg().

if (block == 0) {
/* Non-blocking */
microseconds = 0;
Expand Down
11 changes: 6 additions & 5 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,15 @@ lock_acquire_parse_args(PyObject *args, PyObject *kwds,
char *kwlist[] = {"blocking", "timeout", NULL};
int blocking = 1;
PyObject *timeout_obj = NULL;
const _PyTime_t unset_timeout = _PyTime_FromSeconds(-1);

*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)
Expand All @@ -108,7 +109,7 @@ lock_acquire_parse_args(PyObject *args, PyObject *kwds,
}
if (*timeout < 0 && *timeout != unset_timeout) {
PyErr_SetString(PyExc_ValueError,
"timeout value must be positive");
"timeout value must be a non-negative number");
return -1;
}
if (!blocking)
Expand Down
34 changes: 34 additions & 0 deletions Python/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,40 @@ PyThread_set_stacksize(size_t size)
}


int
PyThread_ParseTimeoutArg(PyObject *arg, int blocking, PY_TIMEOUT_T *timeout_p)
{
assert(_PyTime_FromSeconds(-1) == PyThread_UNSET_TIMEOUT);
if (arg == NULL || arg == Py_None) {
*timeout_p = blocking ? PyThread_UNSET_TIMEOUT : 0;
return 0;
}
if (!blocking) {
PyErr_SetString(PyExc_ValueError,
"can't specify a timeout for a non-blocking call");
return -1;
}

_PyTime_t timeout;
if (_PyTime_FromSecondsObject(&timeout, arg, _PyTime_ROUND_TIMEOUT) < 0) {
return -1;
}
if (timeout < 0) {
PyErr_SetString(PyExc_ValueError,
"timeout value must be a non-negative number");
return -1;
}

if (_PyTime_AsMicroseconds(timeout,
_PyTime_ROUND_TIMEOUT) > PY_TIMEOUT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"timeout value is too large");
return -1;
}
*timeout_p = timeout;
return 0;
}

PyLockStatus
PyThread_acquire_lock_timed_with_retries(PyThread_type_lock lock,
PY_TIMEOUT_T timeout)
Expand Down