exception handling and conservative GC
Filip Pizlo
pizlo@mac.com
Thu Jan 19 01:18:00 GMT 2006
Hello,
The method of allocation used for exceptions in flight (see
__cxa_allocate_exception and __cxa_free_exception) makes things
difficult for garbage collector users. The problem appears whether
or not the collector overrides malloc/free. In particular:
1) If the collector overrides malloc/free with its own allocation
routines, we run into the deeper problem that the collector has no
way of finding thread-local storage. So, we run the risk of freeing
the exception object prematurely. (This is a problem in other
libraries as well.)
2) If the collector does not override malloc/free, then the collector
doesn't see the exception object. The result is that if the user's
exception contained pointers to objects allocated by the GC, those
objects may be freed prematurely.
I attach a program that demonstrates this. It prints the wrong
output, "foobar!", on all of the platforms that I've tested (Mac OS X
with GCC 3.3 and 4.0; I tested both FSF and Apple versions of 4.0;
and also Linux on AMD64 using GCC 3.4). In this program, the
exception object is the char* pointer; its target is allocated using
the GC.
What I have implemented is a hook in eh_alloc.cc (see attached patch)
that resolves this issue. It works regardless of whether the
collector is overriding malloc/free. It allows the collector to
insert its own allocation routines specifically for use with
exceptions; in this case the collector can replace malloc with
GC_malloc_uncollectable and free with GC_free. The result is that:
1) the collector can scan the exception object, and
2) the collector knows not to free the exception object until
explicitly told to do so by libstdc++. Hence, even without a general
solution to the problem of thread-local storage, this resolves the
exception handling issue.
This patch is imperfect: there is no safe way to change the
allocation routines if an exception is already in flight, and I'm not
sure about my namespacing decisions.
Regards,
Filip Pizlo
Computer Science Dept, Purdue University, Lafayette, IN.
The Program:
#include <gc/gc.h>
#include <string.h>
#include <stdio.h>
struct myexc {
char *str;
};
void f() {
myexc e;
e.str=(char*)GC_MALLOC_ATOMIC(100);
strcpy(e.str,"hello, world!");
// uncomment for a cheap workaround
//char **strptr=(char**)GC_MALLOC_UNCOLLECTABLE(sizeof(char**));
//*strptr=e.str;
throw e;
}
void g() {
try {
f();
} catch (...) {
// if you don't see the bug, change 1000 to a bigger number
or do something else here
// that would force a GC. to see correct behavior, comment
this loop out.
for (unsigned i=0;i<1000;++i) {
char *str=(char*)GC_MALLOC_ATOMIC(100);
strcpy(str,"foobar!");
}
throw;
}
}
int main() {
GC_INIT();
try {
g();
} catch (const myexc &e) {
printf("string is: %s\n",e.str);
}
return 0;
}
The Patch:
*** /Users/pizlo/Programs/cvs_hacking/gcc-4.0.2/libstdc++-v3/libsupc+
+/eh_alloc.cc Fri Feb 18 21:35:24 2005
--- libsupc++/eh_alloc.cc Tue Jan 17 01:28:21 2006
***************
*** 52,57 ****
--- 52,80 ----
extern "C" int memset (void *, int, std::size_t);
#endif
+ namespace __gnu_cxx {
+ void *(*__cxa_eh_malloc)(std::size_t)=0;
+ void (*__cxa_eh_free)(void*)=0;
+ }
+
+ using namespace __gnu_cxx;
+
+ static void *my_malloc(std::size_t s) {
+ if (__cxa_eh_malloc) {
+ return __cxa_eh_malloc(s);
+ } else {
+ return malloc(s);
+ }
+ }
+
+ static void my_free(void *p) {
+ if (__cxa_eh_free) {
+ return __cxa_eh_free(p);
+ } else {
+ return free(p);
+ }
+ }
+
using namespace __cxxabiv1;
// ??? How to control these parameters.
***************
*** 112,118 ****
void *ret;
thrown_size += sizeof (__cxa_exception);
! ret = malloc (thrown_size);
if (! ret)
{
--- 135,141 ----
void *ret;
thrown_size += sizeof (__cxa_exception);
! ret = my_malloc (thrown_size);
if (! ret)
{
***************
*** 178,182 ****
#endif
}
else
! free (ptr - sizeof (__cxa_exception));
}
--- 201,205 ----
#endif
}
else
! my_free (ptr - sizeof (__cxa_exception));
}
More information about the Libstdc++
mailing list