summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Python/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/Python/thread.c')
-rw-r--r--contrib/tools/python3/Python/thread.c108
1 files changed, 106 insertions, 2 deletions
diff --git a/contrib/tools/python3/Python/thread.c b/contrib/tools/python3/Python/thread.c
index 7fc53f9b613..b31d1dc5e77 100644
--- a/contrib/tools/python3/Python/thread.c
+++ b/contrib/tools/python3/Python/thread.c
@@ -6,17 +6,38 @@
Stuff shared by all thread_*.h files is collected here. */
#include "Python.h"
+#include "pycore_ceval.h" // _PyEval_MakePendingCalls()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_structseq.h" // _PyStructSequence_FiniBuiltin()
-#include "pycore_pythread.h"
+#include "pycore_pythread.h" // _POSIX_THREADS
#ifndef DONT_HAVE_STDIO_H
-#include <stdio.h>
+# include <stdio.h>
#endif
#include <stdlib.h>
+// Define PY_TIMEOUT_MAX constant.
+#ifdef _POSIX_THREADS
+ // PyThread_acquire_lock_timed() uses (us * 1000) to convert microseconds
+ // to nanoseconds.
+# define PY_TIMEOUT_MAX_VALUE (LLONG_MAX / 1000)
+#elif defined (NT_THREADS)
+ // WaitForSingleObject() accepts timeout in milliseconds in the range
+ // [0; 0xFFFFFFFE] (DWORD type). INFINITE value (0xFFFFFFFF) means no
+ // timeout. 0xFFFFFFFE milliseconds is around 49.7 days.
+# if 0xFFFFFFFELL < LLONG_MAX / 1000
+# define PY_TIMEOUT_MAX_VALUE (0xFFFFFFFELL * 1000)
+# else
+# define PY_TIMEOUT_MAX_VALUE LLONG_MAX
+# endif
+#else
+# define PY_TIMEOUT_MAX_VALUE LLONG_MAX
+#endif
+const long long PY_TIMEOUT_MAX = PY_TIMEOUT_MAX_VALUE;
+
+
static void PyThread__init_thread(void); /* Forward */
#define initialized _PyRuntime.threads.initialized
@@ -72,6 +93,89 @@ 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)
+{
+ 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;
+}
+
+
/* Thread Specific Storage (TSS) API
Cross-platform components of TSS API implementation.