/* 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 acquire_timed(PyThread_type_lock lock, _PyTime_t timeout) { PyLockStatus r; _PyTime_t endtime = 0; _PyTime_t microseconds; if (timeout > 0) endtime = _PyTime_GetMonotonicClock() + timeout; do { 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 (Py_MakePendingCalls() < 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 = endtime - _PyTime_GetMonotonicClock(); /* 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. */
static int pysleep(_PyTime_t secs) { _PyTime_t deadline, monotonic; #ifndef MS_WINDOWS struct timeval timeout; int err = 0; #else _PyTime_t millisecs; unsigned long ul_millis; DWORD rc; HANDLE hInterruptEvent; #endif deadline = _PyTime_GetMonotonicClock() + secs; do { #ifndef MS_WINDOWS if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) return -1; Py_BEGIN_ALLOW_THREADS err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout); Py_END_ALLOW_THREADS if (err == 0) break; if (errno != EINTR) { PyErr_SetFromErrno(PyExc_OSError); return -1; } #else millisecs = _PyTime_AsMilliseconds(secs, _PyTime_ROUND_CEILING); if (millisecs > (double)ULONG_MAX) { PyErr_SetString(PyExc_OverflowError, "sleep length is too large"); return -1; } /* Allow sleep(0) to maintain win32 semantics, and as decreed * by Guido, only the main thread can be interrupted. */ ul_millis = (unsigned long)millisecs; if (ul_millis == 0 || !_PyOS_IsMainThread()) { Py_BEGIN_ALLOW_THREADS Sleep(ul_millis); Py_END_ALLOW_THREADS break; } hInterruptEvent = _PyOS_SigintEvent(); ResetEvent(hInterruptEvent); Py_BEGIN_ALLOW_THREADS rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE); Py_END_ALLOW_THREADS if (rc != WAIT_OBJECT_0) break; #endif /* sleep was interrupted by SIGINT */ if (PyErr_CheckSignals()) return -1; monotonic = _PyTime_GetMonotonicClock(); secs = deadline - monotonic; if (secs < 0) break; /* retry with the recomputed delay */ } while (1);
static PyObject * _queue_SimpleQueue_get_impl(simplequeueobject *self, int block, PyObject *timeout) /*[clinic end generated code: output=ec82a7157dcccd1a input=4bf691f9f01fa297]*/ { _PyTime_t endtime = 0; _PyTime_t timeout_val; PyObject *item; PyLockStatus r; PY_TIMEOUT_T microseconds; if (block == 0) { /* Non-blocking */ microseconds = 0; } else if (timeout != Py_None) { /* With timeout */ if (_PyTime_FromSecondsObject(&timeout_val, timeout, _PyTime_ROUND_CEILING) < 0) return NULL; if (timeout_val < 0) { PyErr_SetString(PyExc_ValueError, "'timeout' must be a non-negative number"); return NULL; } microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING); if (microseconds >= PY_TIMEOUT_MAX) { PyErr_SetString(PyExc_OverflowError, "timeout value is too large"); return NULL; } endtime = _PyTime_GetMonotonicClock() + timeout_val; } else { /* Infinitely blocking */ microseconds = -1; } /* put() signals the queue to be non-empty by releasing the lock. * So we simply try to acquire the lock in a loop, until the condition * (queue non-empty) becomes true. */ while (self->lst_pos == PyList_GET_SIZE(self->lst)) { /* First a simple non-blocking try without releasing the GIL */ r = PyThread_acquire_lock_timed(self->lock, 0, 0); if (r == PY_LOCK_FAILURE && microseconds != 0) { Py_BEGIN_ALLOW_THREADS r = PyThread_acquire_lock_timed(self->lock, microseconds, 1); Py_END_ALLOW_THREADS } if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) { return NULL; } if (r == PY_LOCK_FAILURE) { /* Timed out */ PyErr_SetNone(EmptyError); return NULL; } self->locked = 1; /* Adjust timeout for next iteration (if any) */ if (endtime > 0) { timeout_val = endtime - _PyTime_GetMonotonicClock(); microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING); } }