diff options
Diffstat (limited to 'Python/ceval.c')
-rw-r--r-- | Python/ceval.c | 189 |
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); } |