Re: visit_decref: Assertion `gc->gc.gc_refs != 0' failed.

Jan Urbański <[email protected]> Sat, 27 Mar 2010 03:17:50 +0100
Newsgroups gmane.comp.python.db.psycopg.devel
Message-ID <[email protected]>
On 27/03/10 01:12, Jan Urbański wrote:
> On 23/03/10 16:39, Michael Tharp wrote:
>> Seeing python bomb out on a GC-related assertion during some heavy load
>> testing:
>>
>> #3 0x000000000048bd2c in visit_decref (op=0x7f7b78037c58,
>> data=<value optimized out>) at Modules/gcmodule.c:275
>> __PRETTY_FUNCTION__ = "visit_decref"

> could you provide a self-contained test case? I'm getting a very similar
> segfault and I'm not sure it's because of my patches to psycopg2 or
> would this happen in a vanilla build as well

Here's a patch that fixes my problem. The issue seemed to be that 
psycopg2 was storing the asynchronous cursor in conn->async_cursor, and 
then the connection_traverse function was visiting conn->async_cursor 
and unreffing it, after which the cursor itself was being unreffed and 
that was messing up reference counting.

I was able to trigger an assertion error using a Py_DEBUG build of 
Python2.5 and the following script:

import psycopg2
import tests

conn = psycopg2.connect(tests.dsn)
cur = conn.cursor()
cur.execute("select 1", async=True)
del conn

Results:

python: Modules/gcmodule.c:276: visit_decref: Assertion `gc->gc.gc_refs 
!= 0' failed.
Aborted

On a non-debug build I couldn't make it error, but compiling psycopg2 
with PSYCOPG_DEBUG and then looking at the results of running this 
script with PSYCOPG_DEBUG=1 I see:

[14619] connection_dealloc: deleted connection object at 0xb7667824, 
refcnt = 0
[14619] cursor_dealloc: deleted cursor object at 0x93c1b8c, refcnt = -1

Which clearly is wrong.

I guess you're not using async cursors, so this might not fix your 
problem, but it fixes mine so until you send in a reproducible testcase 
my interest is significantly lower ;)

Jan

_______________________________________________
Psycopg mailing list
Psycopg-IAPFreCvJWPBWskQ1e/[email protected]
http://lists.initd.org/mailman/listinfo/psycopg
0001-Ref-the-async-cursor-before-storing-it-in-the-connec.patch (text/x-diff, 1.3 KB)
From 9e2866e52845bb88a993d92115819bb7c44f31be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Urba=C5=84ski?= <[email protected]>
Date: Sat, 27 Mar 2010 03:17:22 +0100
Subject: [PATCH] Ref the async cursor before storing it in the connection

---
 psycopg/cursor_type.c |    1 +
 psycopg/pqpath.c      |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c
index 4c66a06..bc7c4e9 100644
--- a/psycopg/cursor_type.c
+++ b/psycopg/cursor_type.c
@@ -1517,6 +1517,7 @@ psyco_curs_isready(cursorObject *self, PyObject *args)
         Py_BEGIN_ALLOW_THREADS;
         pthread_mutex_lock(&(self->conn->lock));
         self->pgres = PQgetResult(self->conn->pgconn);
+        Py_XDECREF(self->conn->async_cursor);
         self->conn->async_cursor = NULL;
         pthread_mutex_unlock(&(self->conn->lock));
         Py_END_ALLOW_THREADS;
diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c
index 8b0e134..835859f 100644
--- a/psycopg/pqpath.c
+++ b/psycopg/pqpath.c
@@ -706,6 +706,7 @@ pq_execute(cursorObject *curs, const char *query, int async)
         if (pq_fetch(curs) == -1) return -1;
     }
     else {
+        //Py_INCREF(curs);
         curs->conn->async_cursor = (PyObject*)curs;
     }
 
-- 
1.7.0