Sat 14 Nov 2009 10:19:48 AM UTC, original submission:
A gc-related bug occurs in make_stdio_stream or any similar function which follows the same pattern:
obj_t make_stdio_stream(FILE f, obj_t descr, obj_t input, obj_t *output)
{
struct stdio_handle h = (struct stdio_handle ) chk_malloc(sizeof *h);
h->f = f;
h->descr = descr;
return cobj((void *) h, stream_t, &stdio_ops.cobj_ops);
}
In this function, a struct (an object outside of the managed object system) is allocated, and an object reference is placed into it: h->descr = descr. At this point, the compiler may decide that h->descr is the only place which has a reference to that object. This is a problem if the object was constructed right inside the call to the function, as happens in txr.c:
yyin_stream = make_stdio_stream(in, string_utf8(*argv), t, nil);
The garbage collector knows nothing about structure h, and so if the cobj function invokes gc, h->descr is reclaimed.
Only when h is connected into the allocated cobj does h->descr become reachable, thanks to the mark function in stdio_ops.cobj_ops. But the gc call may take place while the cobj is being allocated, before the h structure is hooked into it.
This issue reproduced when I compiled with an older gcc on i686 with -O2 and -p for profiling.
The following code rearrangement makes it go away:
obj_t make_stdio_stream(FILE f, obj_t descr, obj_t input, obj_t *output)
{
struct stdio_handle h = (struct stdio_handle ) chk_malloc(sizeof *h);
obj_t stream = cobj((void ) h, stream_t, &stdio_ops.cobj_ops);
h->f = f;
h->descr = descr;
return stream;
}
Now the cobj with handle h is prepared first, before descr is stashed into the handle structure. So during the cobj call, the descr ref is maintained in a register or on the stack: places visible to gc.
|