[Zope3-dev] Zope3+W2k+mingw=Python crash

Tim Peters tim at zope.com
Sat Jan 17 20:07:41 EST 2004


[Dave Harris]
> I would like to thank everyone for politely ignoring this post and
> not stating the obvious...
>
> Dave Harris is clueless!

That wasn't my impression, but trying to understand an English description
of a detailed code problem on a platform I don't use is proving pretty much
impossible.

> I completely misapprended the problem. Instead of a code problem, the
> abort occurs during maintenance of the garbage collection chain. In
> particular, cm_dealloc() makes the call:
>
>    _PyObject_GC_UNTRACK((PyObject *)cm);
>
> and the process collapses when the assignment to the previous element
> link is attempted. The address of the previous element appears to be
> 0xfffffffd (-3). Now I have to find out why.

Descriptions in terms of the actual C structures and code would really help.
The gc header is declared like so:

typedef union _gc_head {
	struct {
		union _gc_head *gc_next;
		union _gc_head *gc_prev;
		int gc_refs;
	} gc;
	long double dummy;  /* force worst-case alignment */
} PyGC_Head;

By "assignment to the previous element link", I'm betting you mean one of
these two lines from _PyObject_GC_UNTRACK:

	g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \
	g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \

but I'm not sure which one.  It would be very helpful to know what's in
*all* the members of cm's PyGC_Head header.

Without those details it requires too much pure guessing, but I'm betting
the -3 you're seeing isn't an accident, and am about to milk that for all
it's worth <wink -- but it could be worth a lot!>.

gc.gc_prev is adjacent to gc.gc_refs in the PyGC_Head structure, and the
preprocessor #defines right before the declaration of this struct establish
some special values that appear in gc.gc_refs:

#define _PyGC_REFS_UNTRACKED                    (-2)
#define _PyGC_REFS_REACHABLE	                  (-3)
#define _PyGC_REFS_TENTATIVELY_UNREACHABLE      (-4)

So -3 is _PyGC_REFS_REACHABLE, and is the value we *expect* gc.gc_refs to
have for a tracked object when cyclic gc isn't currently running.  It sounds
like the compiler you're using is confusing it with gc.gc_prev.  This
*could* be because the compiler you're using forces different alignment than
MSVC, perhaps because it has a different belief about what "long double"
means, or maybe just because it uses different alignment rules.  The

	PyGC_Head *g = _Py_AS_GC(o); \

with which _PyObject_GC_TRACK(o) begins is defined as

#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)

and if your compiler disagrees about sizeof(PyGC_Head), accessing fields of
g won't work as intended.  sizeof(PyGC_Head) is 16 under MSVC.  Under most
32-bit compilers used on Linux, I believes it's 12.  If it's 12 under your
compiler, then I'm betting it looks like so:

                   MSVC         yours
                   -------      -----
    (char*)cm-16   gc_next      -out of bounds-
    (char*)cm-12   gc_prev      gc_next
    (char*)cm-8    gc_refs      gc_prev
    (char*)cm-4    -padding-    gc_refs

and that would explain why a reference to gc_prev compiled by your compiler
actually picks up gc_refs in a PyGC_Head filled in by code compiled under
MSVC.

If that's what's really happening, then I'm afraid you'll have to build
Python with this compiler too (+ extension modules), or find some way to
convince your compiler to use the same alignment rules as MSVC.

There's one other possibility:  Zope's C code is traditionally careless
about following the rules for extension modules, particularly in respecting
the rule that extension modules should never use the macro spellings of
Python C API functions (those are technically reserved for use by the Python
core -- but with some irritating exceptions, which makes the rules darned
hard to remember).

If the above is really what's happening, then if instead of cm_dealloc
doing:

    _PyObject_GC_UNTRACK((PyObject *)cm);

it did what it's supposed to do:

    PyObject_GC_UnTrack((PyObject *)cm);

then none of the *relevant* code would have been compiled by your compiler
(it would have called into the MSVC-compiled PyObject_GC_UnTrack external
function, which (of course) has the same belief about sizeof(PyGC_Head) as
all other MSVC-compiled code).




More information about the Zope3-dev mailing list