From 7022269b3dfc6640308ed4b878900fee45428af2 Mon Sep 17 00:00:00 2001 From: Federico Di Gregorio Date: Sun, 4 Oct 2009 12:02:02 +0200 Subject: [PATCH] Fixed a deadlock when using the same connection from multiple threads --- ChangeLog | 5 ++++ psycopg/connection_int.c | 56 +++++++++++++++++++++++++++++---------- psycopg/connection_type.c | 2 -- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index b0cc06d7..8153d2c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2009-10-04 Federico Di Gregorio + + * psycopg/connection_int.c: applied patch from Richard Davies to avoid + deadlocks with multiple threads using the same connection. + 2009-08-08 Federico Di Gregorio * Release 2.0.12. diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index cb41a758..a78a005e 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -64,11 +64,14 @@ conn_notice_callback(void *args, const char *message) void conn_notice_process(connectionObject *self) { + Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); struct connectionObject_notice *notice = self->notice_pending; - + while (notice != NULL) { + Py_BLOCK_THREADS; + PyObject *msg = PyString_FromString(notice->message); Dprintf("conn_notice_process: %s", notice->message); @@ -79,22 +82,26 @@ conn_notice_process(connectionObject *self) /* Remove the oldest item if the queue is getting too long. */ if (PyList_GET_SIZE(self->notice_list) > CONN_NOTICES_LIMIT) PySequence_DelItem(self->notice_list, 0); - + + Py_UNBLOCK_THREADS; + notice = notice->next; } - + pthread_mutex_unlock(&self->lock); - - conn_notice_clean(self); + Py_END_ALLOW_THREADS; + + conn_notice_clean(self); } void conn_notice_clean(connectionObject *self) { + Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); - + struct connectionObject_notice *tmp, *notice = self->notice_pending; - + while (notice != NULL) { tmp = notice; notice = notice->next; @@ -104,7 +111,8 @@ conn_notice_clean(connectionObject *self) self->notice_pending = NULL; - pthread_mutex_unlock(&self->lock); + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; } /* conn_setup - setup and read basic information about the connection */ @@ -112,6 +120,10 @@ conn_notice_clean(connectionObject *self) int conn_setup(connectionObject *self, PGconn *pgconn) { + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + Py_BLOCK_THREADS; + PGresult *pgres; const char *data, *tmp; const char *scs; /* standard-conforming strings */ @@ -160,26 +172,32 @@ conn_setup(connectionObject *self, PGconn *pgconn) Dprintf("conn_connect: server requires E'' quotes: %s", self->equote ? "YES" : "NO"); - Py_BEGIN_ALLOW_THREADS; + Py_UNBLOCK_THREADS; pgres = PQexec(pgconn, datestyle); - Py_END_ALLOW_THREADS; + Py_BLOCK_THREADS; if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) { PyErr_SetString(OperationalError, "can't set datestyle to ISO"); PQfinish(pgconn); IFCLEARPGRES(pgres); + Py_UNBLOCK_THREADS; + pthread_mutex_unlock(&self->lock); + Py_BLOCK_THREADS; return -1; } CLEARPGRES(pgres); - Py_BEGIN_ALLOW_THREADS; + Py_UNBLOCK_THREADS; pgres = PQexec(pgconn, encoding); - Py_END_ALLOW_THREADS; + Py_BLOCK_THREADS; if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) { PyErr_SetString(OperationalError, "can't fetch client_encoding"); PQfinish(pgconn); IFCLEARPGRES(pgres); + Py_UNBLOCK_THREADS; + pthread_mutex_unlock(&self->lock); + Py_BLOCK_THREADS; return -1; } tmp = PQgetvalue(pgres, 0, 0); @@ -188,6 +206,9 @@ conn_setup(connectionObject *self, PGconn *pgconn) PyErr_NoMemory(); PQfinish(pgconn); IFCLEARPGRES(pgres); + Py_UNBLOCK_THREADS; + pthread_mutex_unlock(&self->lock); + Py_BLOCK_THREADS; return -1; } for (i=0 ; i < strlen(tmp) ; i++) @@ -195,15 +216,18 @@ conn_setup(connectionObject *self, PGconn *pgconn) self->encoding[i] = '\0'; CLEARPGRES(pgres); - Py_BEGIN_ALLOW_THREADS; + Py_UNBLOCK_THREADS; pgres = PQexec(pgconn, isolevel); - Py_END_ALLOW_THREADS; + Py_BLOCK_THREADS; if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) { PyErr_SetString(OperationalError, "can't fetch default_isolation_level"); PQfinish(pgconn); IFCLEARPGRES(pgres); + Py_UNBLOCK_THREADS; + pthread_mutex_unlock(&self->lock); + Py_BLOCK_THREADS; return -1; } data = PQgetvalue(pgres, 0, 0); @@ -217,6 +241,10 @@ conn_setup(connectionObject *self, PGconn *pgconn) self->isolation_level = 2; CLEARPGRES(pgres); + Py_UNBLOCK_THREADS; + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + return 0; } diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 4e6b42f0..2e04ad22 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -366,9 +366,7 @@ psyco_conn_reset(connectionObject *self) if (pq_reset(self) < 0) return NULL; - pthread_mutex_lock(&self->lock); res = conn_setup(self, self->pgconn); - pthread_mutex_unlock(&self->lock); if (res < 0) return NULL;