mirror of https://git.kore.io/kore.git
Python: improve kore.lock when handling cancelled coroutines.
If a coroutine is killed from another coroutine and the killed coroutine was waiting on a kore.lock() object, it would have been incorrectly woken up again once said lock was released. This would cause a Python exception that a generator was already running and a crash due to the pool element already being freed. Track the active locking operation per coroutine so we can remove the coroutine if it is killed, fixing the problem.
This commit is contained in:
parent
8235759bca
commit
bb2f0d8b52
|
@ -24,6 +24,7 @@ struct python_coro {
|
||||||
PyObject *obj;
|
PyObject *obj;
|
||||||
char *name;
|
char *name;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
struct pylock_op *lockop;
|
||||||
struct pysocket_op *sockop;
|
struct pysocket_op *sockop;
|
||||||
struct pygather_op *gatherop;
|
struct pygather_op *gatherop;
|
||||||
struct http_request *request;
|
struct http_request *request;
|
||||||
|
|
25
src/python.c
25
src/python.c
|
@ -410,6 +410,13 @@ kore_python_coro_delete(void *obj)
|
||||||
Py_DECREF(coro->obj);
|
Py_DECREF(coro->obj);
|
||||||
coro_running = NULL;
|
coro_running = NULL;
|
||||||
|
|
||||||
|
if (coro->lockop != NULL) {
|
||||||
|
coro->lockop->active = 0;
|
||||||
|
TAILQ_REMOVE(&coro->lockop->lock->ops, coro->lockop, list);
|
||||||
|
Py_DECREF((PyObject *)coro->lockop);
|
||||||
|
coro->lockop = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (coro->state == CORO_STATE_RUNNABLE)
|
if (coro->state == CORO_STATE_RUNNABLE)
|
||||||
TAILQ_REMOVE(&coro_runnable, coro, list);
|
TAILQ_REMOVE(&coro_runnable, coro, list);
|
||||||
else
|
else
|
||||||
|
@ -947,6 +954,7 @@ python_coro_create(PyObject *obj, struct http_request *req)
|
||||||
coro->name = NULL;
|
coro->name = NULL;
|
||||||
coro->result = NULL;
|
coro->result = NULL;
|
||||||
coro->sockop = NULL;
|
coro->sockop = NULL;
|
||||||
|
coro->lockop = NULL;
|
||||||
coro->gatherop = NULL;
|
coro->gatherop = NULL;
|
||||||
coro->exception = NULL;
|
coro->exception = NULL;
|
||||||
coro->exception_msg = NULL;
|
coro->exception_msg = NULL;
|
||||||
|
@ -3575,6 +3583,7 @@ pylock_dealloc(struct pylock *lock)
|
||||||
while ((op = TAILQ_FIRST(&lock->ops)) != NULL) {
|
while ((op = TAILQ_FIRST(&lock->ops)) != NULL) {
|
||||||
TAILQ_REMOVE(&lock->ops, op, list);
|
TAILQ_REMOVE(&lock->ops, op, list);
|
||||||
op->active = 0;
|
op->active = 0;
|
||||||
|
op->coro->lockop = NULL;
|
||||||
Py_DECREF((PyObject *)op);
|
Py_DECREF((PyObject *)op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3615,6 +3624,9 @@ pylock_aenter(struct pylock *lock, PyObject *args)
|
||||||
{
|
{
|
||||||
struct pylock_op *op;
|
struct pylock_op *op;
|
||||||
|
|
||||||
|
if (coro_running->lockop != NULL)
|
||||||
|
fatal("%s: lockop not NULL for %u", __func__, coro_running->id);
|
||||||
|
|
||||||
if (lock->owner != NULL && lock->owner->id == coro_running->id) {
|
if (lock->owner != NULL && lock->owner->id == coro_running->id) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "recursive lock detected");
|
PyErr_SetString(PyExc_RuntimeError, "recursive lock detected");
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
@ -3628,6 +3640,8 @@ pylock_aenter(struct pylock *lock, PyObject *args)
|
||||||
op->locking = 1;
|
op->locking = 1;
|
||||||
op->coro = coro_running;
|
op->coro = coro_running;
|
||||||
|
|
||||||
|
coro_running->lockop = op;
|
||||||
|
|
||||||
Py_INCREF((PyObject *)op);
|
Py_INCREF((PyObject *)op);
|
||||||
Py_INCREF((PyObject *)lock);
|
Py_INCREF((PyObject *)lock);
|
||||||
|
|
||||||
|
@ -3641,6 +3655,9 @@ pylock_aexit(struct pylock *lock, PyObject *args)
|
||||||
{
|
{
|
||||||
struct pylock_op *op;
|
struct pylock_op *op;
|
||||||
|
|
||||||
|
if (coro_running->lockop != NULL)
|
||||||
|
fatal("%s: lockop not NULL for %u", __func__, coro_running->id);
|
||||||
|
|
||||||
if (lock->owner == NULL || lock->owner->id != coro_running->id) {
|
if (lock->owner == NULL || lock->owner->id != coro_running->id) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "invalid lock owner");
|
PyErr_SetString(PyExc_RuntimeError, "invalid lock owner");
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
@ -3654,6 +3671,8 @@ pylock_aexit(struct pylock *lock, PyObject *args)
|
||||||
op->locking = 0;
|
op->locking = 0;
|
||||||
op->coro = coro_running;
|
op->coro = coro_running;
|
||||||
|
|
||||||
|
coro_running->lockop = op;
|
||||||
|
|
||||||
Py_INCREF((PyObject *)op);
|
Py_INCREF((PyObject *)op);
|
||||||
Py_INCREF((PyObject *)lock);
|
Py_INCREF((PyObject *)lock);
|
||||||
|
|
||||||
|
@ -3674,7 +3693,8 @@ pylock_do_release(struct pylock *lock)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
op->active = 0;
|
op->active = 0;
|
||||||
TAILQ_REMOVE(&op->lock->ops, op, list);
|
op->coro->lockop = NULL;
|
||||||
|
TAILQ_REMOVE(&lock->ops, op, list);
|
||||||
|
|
||||||
if (op->coro->request != NULL)
|
if (op->coro->request != NULL)
|
||||||
http_request_wakeup(op->coro->request);
|
http_request_wakeup(op->coro->request);
|
||||||
|
@ -3694,6 +3714,8 @@ pylock_op_dealloc(struct pylock_op *op)
|
||||||
op->active = 0;
|
op->active = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
op->coro->lockop = NULL;
|
||||||
|
|
||||||
Py_DECREF((PyObject *)op->lock);
|
Py_DECREF((PyObject *)op->lock);
|
||||||
PyObject_Del((PyObject *)op);
|
PyObject_Del((PyObject *)op);
|
||||||
}
|
}
|
||||||
|
@ -3741,6 +3763,7 @@ pylock_op_iternext(struct pylock_op *op)
|
||||||
|
|
||||||
if (op->active) {
|
if (op->active) {
|
||||||
op->active = 0;
|
op->active = 0;
|
||||||
|
op->coro->lockop = NULL;
|
||||||
TAILQ_REMOVE(&op->lock->ops, op, list);
|
TAILQ_REMOVE(&op->lock->ops, op, list);
|
||||||
Py_DECREF((PyObject *)op);
|
Py_DECREF((PyObject *)op);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue