Re: psycopg2 2.0.8 - segmentation fault
"Gangadharan S.A." <[email protected]>
| Newsgroups | gmane.comp.python.db.psycopg.devel |
|---|---|
| Message-ID | <[email protected]> |
> > Program received signal SIGABRT, Aborted. > [Switching to Thread 1283557696 (LWP 12437)] > 0x00002b2fdc478535 in raise () from /lib64/libc.so.6 > (gdb) bt > #0 0x00002b2fdc478535 in raise () from /lib64/libc.so.6 > #1 0x00002b2fdc479990 in abort () from /lib64/libc.so.6 > #2 0x00002b2fdc4af6db in __libc_message () from /lib64/libc.so.6 > #3 0x00002b2fdc4b48fe in malloc_printerr () from /lib64/libc.so.6 > #4 0x00002b2fdc4b5f36 in free () from /lib64/libc.so.6 > #5 0x00002b2fe062f7e7 in connection_dealloc (obj=0x2aaaaac0ef30) at > psycopg/connection_type.c:464 I managed to reproduce and fix the issue. This is happening because when dealloc-ing, the "Py_BEGIN_ALLOW_THREADS;" in conn_close is letting other threads launch the garbage collector which in turn ends up running the dealloc a second time. The fix is to untrack the connnection object from GC, before going ahead with dealloc, as said in http://docs.python.org/c-api/gcsupport.html "Similarly, the deallocator for the object must conform to a similar pair of rules: 1. Before fields which refer to other containers are invalidated, PyObject_GC_UnTrack<http://docs.python.org/c-api/gcsupport.html#PyObject_GC_UnTrack>must be called. 2. The object’s memory must be deallocated using PyObject_GC_Del<http://docs.python.org/c-api/gcsupport.html#PyObject_GC_Del> ." I have attached script to reproduce the issue and the 1 line patch against current trunk to fix it. Thanks, Gangadharan _______________________________________________ Psycopg mailing list Psycopg-IAPFreCvJWPBWskQ1e/[email protected] http://lists.initd.org/mailman/listinfo/psycopg
double_dealloc_fix.diff
(text/x-patch, 355 B)
=== modified file 'psycopg/connection_type.c'
--- psycopg/connection_type.c 2009-04-04 17:17:40 +0000
+++ psycopg/connection_type.c 2009-04-15 16:51:49 +0000
@@ -461,6 +461,8 @@
{
connectionObject *self = (connectionObject *)obj;
+ PyObject_GC_UnTrack(self);
+
if (self->closed == 0) conn_close(self);
conn_notice_clean(self);
trigger_double_dealloc.py
(text/x-python, 1.9 KB)
import psycopg2, psycopg2.extensions
import threading
import gc
import time
import sys
# inherit psycopg2 connection class just so that
# garbage collector enters the tp_clear code path
# in delete_garbage()
class my_connection(psycopg2.extensions.connection):
pass
class db_user(threading.Thread):
def run(self):
conn2 = psycopg2.connect(sys.argv[1], connection_factory=my_connection)
cursor = conn2.cursor()
cursor.execute("UPDATE test_psycopg2_dealloc SET a = 3", async=1)
# the conn2 desctructor will block indefinitely
# on the completion of the query
# (and it will not be holding the GIL during that time)
print >> sys.stderr, "begin conn2 del"
del cursor, conn2
print >> sys.stderr, "end conn2 del"
def main():
# lock out a db row
conn1 = psycopg2.connect(sys.argv[1], connection_factory=my_connection)
cursor = conn1.cursor()
cursor.execute("DROP TABLE IF EXISTS test_psycopg2_dealloc")
cursor.execute("CREATE TABLE test_psycopg2_dealloc (a int)")
cursor.execute("INSERT INTO test_psycopg2_dealloc VALUES (1)")
conn1.commit()
cursor.execute("UPDATE test_psycopg2_dealloc SET a = 2", async=1)
# concurrent thread trying to access the locked row
db_user().start()
# eventually, a gc.collect run will happen
# while the conn2 is inside conn_close()
# but this second dealloc won't get blocked
# as it will avoid conn_close()
for i in range(10):
if gc.collect():
print >> sys.stderr, "garbage collection done"
break
time.sleep(1)
# we now unlock the row by invoking
# the desctructor of conn1. This will permit the
# concurrent thread destructor of conn2 to
# continue and it will end up trying to free
# self->dsn a second time.
print >> sys.stderr, "begin conn1 del"
del cursor, conn1
print >> sys.stderr, "end conn1 del"
if __name__ == '__main__':
main()