summaryrefslogtreecommitdiff
path: root/Python/ceval.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c189
1 files changed, 113 insertions, 76 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index b311248c6a..28e923219d 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -169,23 +169,34 @@ PyEval_ThreadsInitialized(void)
void
PyEval_InitThreads(void)
{
- if (gil_created())
+ if (gil_created()) {
return;
+ }
+
PyThread_init_thread();
create_gil();
take_gil(_PyThreadState_GET());
- _PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
- if (!_PyRuntime.ceval.pending.lock)
- _PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
+
+ _PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
+ if (_PyRuntime.ceval.pending.lock == NULL) {
+ Py_FatalError("Can't initialize threads for pending calls");
+ }
}
void
_PyEval_FiniThreads(void)
{
- if (!gil_created())
+ if (!gil_created()) {
return;
+ }
+
destroy_gil();
assert(!gil_created());
+
+ if (_PyRuntime.ceval.pending.lock != NULL) {
+ PyThread_free_lock(_PyRuntime.ceval.pending.lock);
+ _PyRuntime.ceval.pending.lock = NULL;
+ }
}
void
@@ -243,9 +254,12 @@ PyEval_ReInitThreads(void)
if (!gil_created())
return;
recreate_gil();
- _PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
take_gil(current_tstate);
- _PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
+
+ _PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
+ if (_PyRuntime.ceval.pending.lock == NULL) {
+ Py_FatalError("Can't initialize threads for pending calls");
+ }
/* Destroy all threads except the current one */
_PyThreadState_DeleteExcept(current_tstate);
@@ -323,6 +337,37 @@ _PyEval_SignalReceived(void)
SIGNAL_PENDING_SIGNALS();
}
+/* Push one item onto the queue while holding the lock. */
+static int
+_push_pending_call(struct _pending_calls *pending,
+ int (*func)(void *), void *arg)
+{
+ int i = pending->last;
+ int j = (i + 1) % NPENDINGCALLS;
+ if (j == pending->first) {
+ return -1; /* Queue full */
+ }
+ pending->calls[i].func = func;
+ pending->calls[i].arg = arg;
+ pending->last = j;
+ return 0;
+}
+
+/* Pop one item off the queue while holding the lock. */
+static void
+_pop_pending_call(struct _pending_calls *pending,
+ int (**func)(void *), void **arg)
+{
+ int i = pending->first;
+ if (i == pending->last) {
+ return; /* Queue empty */
+ }
+
+ *func = pending->calls[i].func;
+ *arg = pending->calls[i].arg;
+ pending->first = (i + 1) % NPENDINGCALLS;
+}
+
/* This implementation is thread-safe. It allows
scheduling to be made from any thread, and even from an executing
callback.
@@ -331,52 +376,36 @@ _PyEval_SignalReceived(void)
int
Py_AddPendingCall(int (*func)(void *), void *arg)
{
- int i, j, result=0;
- PyThread_type_lock lock = _PyRuntime.ceval.pending.lock;
-
- /* try a few times for the lock. Since this mechanism is used
- * for signal handling (on the main thread), there is a (slim)
- * chance that a signal is delivered on the same thread while we
- * hold the lock during the Py_MakePendingCalls() function.
- * This avoids a deadlock in that case.
- * Note that signals can be delivered on any thread. In particular,
- * on Windows, a SIGINT is delivered on a system-created worker
- * thread.
- * We also check for lock being NULL, in the unlikely case that
- * this function is called before any bytecode evaluation takes place.
- */
- if (lock != NULL) {
- for (i = 0; i<100; i++) {
- if (PyThread_acquire_lock(lock, NOWAIT_LOCK))
- break;
- }
- if (i == 100)
- return -1;
- }
+ struct _pending_calls *pending = &_PyRuntime.ceval.pending;
- i = _PyRuntime.ceval.pending.last;
- j = (i + 1) % NPENDINGCALLS;
- if (j == _PyRuntime.ceval.pending.first) {
- result = -1; /* Queue full */
- } else {
- _PyRuntime.ceval.pending.calls[i].func = func;
- _PyRuntime.ceval.pending.calls[i].arg = arg;
- _PyRuntime.ceval.pending.last = j;
+ PyThread_acquire_lock(pending->lock, WAIT_LOCK);
+ if (pending->finishing) {
+ PyThread_release_lock(pending->lock);
+
+ PyObject *exc, *val, *tb;
+ PyErr_Fetch(&exc, &val, &tb);
+ PyErr_SetString(PyExc_SystemError,
+ "Py_AddPendingCall: cannot add pending calls "
+ "(Python shutting down)");
+ PyErr_Print();
+ PyErr_Restore(exc, val, tb);
+ return -1;
}
+ int result = _push_pending_call(pending, func, arg);
+ PyThread_release_lock(pending->lock);
+
/* signal main loop */
SIGNAL_PENDING_CALLS();
- if (lock != NULL)
- PyThread_release_lock(lock);
return result;
}
static int
handle_signals(void)
{
- /* Only handle signals on main thread. */
- if (_PyRuntime.ceval.pending.main_thread &&
- PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
- {
+ /* Only handle signals on main thread. PyEval_InitThreads must
+ * have been called already.
+ */
+ if (PyThread_get_thread_ident() != _PyRuntime.main_thread) {
return 0;
}
/*
@@ -396,14 +425,12 @@ handle_signals(void)
}
static int
-make_pending_calls(void)
+make_pending_calls(struct _pending_calls* pending)
{
static int busy = 0;
/* only service pending calls on main thread */
- if (_PyRuntime.ceval.pending.main_thread &&
- PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
- {
+ if (PyThread_get_thread_ident() != _PyRuntime.main_thread) {
return 0;
}
@@ -417,35 +444,20 @@ make_pending_calls(void)
UNSIGNAL_PENDING_CALLS();
int res = 0;
- if (!_PyRuntime.ceval.pending.lock) {
- /* initial allocation of the lock */
- _PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
- if (_PyRuntime.ceval.pending.lock == NULL) {
- res = -1;
- goto error;
- }
- }
-
/* perform a bounded number of calls, in case of recursion */
for (int i=0; i<NPENDINGCALLS; i++) {
- int j;
- int (*func)(void *);
+ int (*func)(void *) = NULL;
void *arg = NULL;
/* pop one item off the queue while holding the lock */
- PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
- j = _PyRuntime.ceval.pending.first;
- if (j == _PyRuntime.ceval.pending.last) {
- func = NULL; /* Queue empty */
- } else {
- func = _PyRuntime.ceval.pending.calls[j].func;
- arg = _PyRuntime.ceval.pending.calls[j].arg;
- _PyRuntime.ceval.pending.first = (j + 1) % NPENDINGCALLS;
- }
- PyThread_release_lock(_PyRuntime.ceval.pending.lock);
+ PyThread_acquire_lock(pending->lock, WAIT_LOCK);
+ _pop_pending_call(pending, &func, &arg);
+ PyThread_release_lock(pending->lock);
+
/* having released the lock, perform the callback */
- if (func == NULL)
+ if (func == NULL) {
break;
+ }
res = func(arg);
if (res) {
goto error;
@@ -461,6 +473,30 @@ error:
return res;
}
+void
+_Py_FinishPendingCalls(void)
+{
+ struct _pending_calls *pending = &_PyRuntime.ceval.pending;
+
+ assert(PyGILState_Check());
+
+ PyThread_acquire_lock(pending->lock, WAIT_LOCK);
+ pending->finishing = 1;
+ PyThread_release_lock(pending->lock);
+
+ if (!_Py_atomic_load_relaxed(&(pending->calls_to_do))) {
+ return;
+ }
+
+ if (make_pending_calls(pending) < 0) {
+ PyObject *exc, *val, *tb;
+ PyErr_Fetch(&exc, &val, &tb);
+ PyErr_BadInternalCall();
+ _PyErr_ChainExceptions(exc, val, tb);
+ PyErr_Print();
+ }
+}
+
/* Py_MakePendingCalls() is a simple wrapper for the sake
of backward-compatibility. */
int
@@ -475,7 +511,7 @@ Py_MakePendingCalls(void)
return res;
}
- res = make_pending_calls();
+ res = make_pending_calls(&_PyRuntime.ceval.pending);
if (res != 0) {
return res;
}
@@ -602,6 +638,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
PyObject **fastlocals, **freevars;
PyObject *retval = NULL; /* Return value */
PyThreadState *tstate = _PyThreadState_GET();
+ _Py_atomic_int *eval_breaker = &_PyRuntime.ceval.eval_breaker;
PyCodeObject *co;
/* when tracing we set things up so that
@@ -687,7 +724,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
#define DISPATCH() \
{ \
- if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.eval_breaker)) { \
+ if (!_Py_atomic_load_relaxed(eval_breaker)) { \
FAST_DISPATCH(); \
} \
continue; \
@@ -989,7 +1026,7 @@ main_loop:
async I/O handler); see Py_AddPendingCall() and
Py_MakePendingCalls() above. */
- if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.eval_breaker)) {
+ if (_Py_atomic_load_relaxed(eval_breaker)) {
opcode = _Py_OPCODE(*next_instr);
if (opcode == SETUP_FINALLY ||
opcode == SETUP_WITH ||
@@ -1024,7 +1061,7 @@ main_loop:
if (_Py_atomic_load_relaxed(
&_PyRuntime.ceval.pending.calls_to_do))
{
- if (make_pending_calls() != 0) {
+ if (make_pending_calls(&_PyRuntime.ceval.pending) != 0) {
goto error;
}
}
@@ -4911,7 +4948,7 @@ import_from(PyObject *v, PyObject *name)
}
x = PyImport_GetModule(fullmodname);
Py_DECREF(fullmodname);
- if (x == NULL) {
+ if (x == NULL && !PyErr_Occurred()) {
goto error;
}
Py_DECREF(pkgname);
@@ -4934,7 +4971,7 @@ import_from(PyObject *v, PyObject *name)
"cannot import name %R from %R (unknown location)",
name, pkgname_or_unknown
);
- /* NULL check for errmsg done by PyErr_SetImportError. */
+ /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */
PyErr_SetImportError(errmsg, pkgname, NULL);
}
else {
@@ -4942,7 +4979,7 @@ import_from(PyObject *v, PyObject *name)
"cannot import name %R from %R (%S)",
name, pkgname_or_unknown, pkgpath
);
- /* NULL check for errmsg done by PyErr_SetImportError. */
+ /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */
PyErr_SetImportError(errmsg, pkgname, pkgpath);
}