This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [exception patch]: Throw bad alloc
- To: Jason Merrill <jason at cygnus dot com>
- Subject: Re: [exception patch]: Throw bad alloc
- From: Nathan Sidwell <nathan at codesourcery dot com>
- Date: Tue, 06 Jun 2000 17:43:49 +0100
- CC: gcc-patches at gcc dot gnu dot org, mark at codesourcery dot com
- Organization: CodeSourcery, LLC
- References: <3933BDB8.1CDD8D03@codesourcery.com> <u9n1l788jp.fsf@decepticon.cygnus.com>
Jason Merrill wrote:
>
> >>>>> Nathan Sidwell <nathan@codesourcery.com> writes:
> No 'extern'.
> This should be ptrdiff_t.
>
> Otherwise, OK.
Ok, I installed the attached, plus the attached testcase,
booted & tested on i686-pc-linux-gnu
nathan
--
Dr Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery LLC
'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
gcc/ChangeLog:
2000-05-29 Nathan Sidwell <nathan@codesourcery.com>
* eh-common.h (EH_ALLOC_SIZE, EH_ALLOC_ALIGN): New #defines.
(eh_context): Add alloc_mask and alloc_buffer emergency fallback
space.
* libgcc2.c (__eh_alloc): Moved from cp/exception.cc. Fallback on
emergency eh_context buffer, if malloc fails.
(__eh_free): Moved from cp/exception.cc. Release to emergency
eh_context buffer, if appropriate.
gcc/cp/ChangeLog:
2000-05-29 Nathan Sidwell <nathan@codesourcery.com>
* exception.cc: (__eh_alloc, __eh_free): Moved to libgcc2.c
Index: eh-common.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/eh-common.h,v
retrieving revision 1.11
diff -c -3 -p -r1.11 eh-common.h
*** eh-common.h 2000/01/14 17:14:42 1.11
--- eh-common.h 2000/06/05 11:58:27
*************** Foundation, 59 Temple Place - Suite 330,
*** 39,44 ****
--- 39,58 ----
The routine get_dynamic_handler_chain() also has a dependancy on
the location of 'dynamic_handler_chain'. If its location is changed,
that routine must be modified as well. */
+ #ifndef EH_ALLOC_SIZE
+ /* 192 bytes means the entire eh_context plus malloc overhead fits in 256
+ bytes (assuming 8 byte pointers). 192 bytes gives an eh_info and object
+ size limit of 96 bytes. This should be sufficient for throwing bad_alloc. */
+ #define EH_ALLOC_SIZE 192
+ #endif
+ #ifndef EH_ALLOC_ALIGN
+ /* We can't use BIGGEST_ALIGNMENT, because on some systems, that expands to
+ a check on a compile time switch like
+ 'target_flags & MASK_ALIGN_DOUBLE ? 64 : 32'. There's no macro for
+ 'largest alignment for any code this compiler can build for', which is
+ really what is needed. */
+ #define EH_ALLOC_ALIGN 16
+ #endif
struct eh_context
{
*************** struct eh_context
*** 48,53 ****
--- 62,71 ----
void *info;
/* This is used to remember where we threw for re-throws */
void *table_index; /* address of exception table entry to rethrow from */
+ /* emergency fallback space, if malloc fails during handling */
+ char alloc_buffer[EH_ALLOC_SIZE]
+ __attribute__((__aligned__(EH_ALLOC_ALIGN)));
+ unsigned alloc_mask;
};
#ifndef EH_TABLE_LOOKUP
Index: libgcc2.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/libgcc2.c,v
retrieving revision 1.94
diff -c -3 -p -r1.94 libgcc2.c
*** libgcc2.c 2000/05/25 15:21:50 1.94
--- libgcc2.c 2000/06/05 11:58:29
*************** eh_context_specific (void)
*** 3190,3195 ****
--- 3190,3264 ----
}
#endif /* __GTHREADS */
+ /* Support routines for alloc/free during exception handling */
+
+ /* __eh_alloc and __eh_free attempt allocation using malloc, but fall back to
+ the small arena in the eh_context. This is needed because throwing an
+ out-of-memory exception would fail otherwise. The emergency space is
+ allocated in blocks of size EH_ALLOC_ALIGN, the
+ minimum allocation being two blocks. A bitmask indicates which blocks
+ have been allocated. To indicate the size of an allocation, the bit for
+ the final block is not set. Hence each allocation is a run of 1s followed
+ by a zero. */
+ void *
+ __eh_alloc (size_t size)
+ {
+ void *p;
+
+ if (!size)
+ abort();
+ p = malloc (size);
+ if (p == 0)
+ {
+ struct eh_context *eh = __get_eh_context ();
+ unsigned blocks = (size + EH_ALLOC_ALIGN - 1) / EH_ALLOC_ALIGN;
+ unsigned real_mask = eh->alloc_mask | (eh->alloc_mask << 1);
+ unsigned our_mask;
+ unsigned ix;
+
+ if (blocks > EH_ALLOC_SIZE / EH_ALLOC_ALIGN)
+ __terminate ();
+ blocks += blocks == 1;
+ our_mask = (1 << blocks) - 1;
+
+ for (ix = EH_ALLOC_SIZE / EH_ALLOC_ALIGN - blocks; ix; ix--)
+ if (! ((real_mask >> ix) & our_mask))
+ {
+ /* found some space */
+ p = &eh->alloc_buffer[ix * EH_ALLOC_ALIGN];
+ eh->alloc_mask |= (our_mask >> 1) << ix;
+ return p;
+ }
+ __terminate ();
+ }
+ return p;
+ }
+
+ /* Free the memory for an cp_eh_info and associated exception, given
+ a pointer to the cp_eh_info. */
+ void
+ __eh_free (void *p)
+ {
+ struct eh_context *eh = __get_eh_context ();
+
+ ptrdiff_t diff = (char *)p - &eh->alloc_buffer[0];
+ if (diff >= 0 && diff < EH_ALLOC_SIZE)
+ {
+ unsigned mask = eh->alloc_mask;
+ unsigned bit = 1 << (diff / EH_ALLOC_ALIGN);
+
+ do
+ {
+ mask ^= bit;
+ bit <<= 1;
+ }
+ while (mask & bit);
+ eh->alloc_mask = mask;
+ }
+ else
+ free (p);
+ }
+
/* Support routines for setjmp/longjmp exception handling. */
/* Calls to __sjthrow are generated by the compiler when an exception
Index: cp/exception.cc
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/exception.cc,v
retrieving revision 1.34
diff -c -3 -p -r1.34 exception.cc
*** exception.cc 2000/06/03 02:20:09 1.34
--- exception.cc 2000/06/05 11:58:30
*************** struct cp_eh_info
*** 118,123 ****
--- 118,127 ----
extern "C" cp_eh_info **__get_eh_info (); // actually void **
+ /* Exception allocate and free, defined in libgcc2. */
+ extern "C" void *__eh_alloc(size_t);
+ extern "C" void __eh_free();
+
/* Is P the type_info node for a pointer of some kind? */
extern bool __is_pointer (void *);
*************** __start_cp_handler (void)
*** 157,185 ****
p->caught = 1;
p->handlers++;
return p;
- }
-
- /* Allocate a buffer for a cp_eh_info and an exception object of size SIZE,
- and return a pointer to the beginning of the object's space. */
-
- extern "C" void * malloc (size_t);
- extern "C" void *
- __eh_alloc (size_t size)
- {
- void *p = malloc (size);
- if (p == 0)
- terminate ();
- return p;
- }
-
- /* Free the memory for an cp_eh_info and associated exception, given
- a pointer to the cp_eh_info. */
-
- extern "C" void free (void *);
- extern "C" void
- __eh_free (void *p)
- {
- free (p);
}
extern "C" int __throw_type_match_rtti_2 (const void *, const void *,
--- 161,166 ----
2000-06-06 Nathan Sidwell <nathan@codesourcery.com>
* g++.old-deja/g++.eh/badalloc1.C: New test.
Index: testsuite/g++.old-deja/g++.eh/badalloc1.C
===================================================================
RCS file: badalloc1.C
diff -N badalloc1.C
*** /dev/null Tue May 5 13:32:27 1998
--- badalloc1.C Tue Jun 6 09:36:34 2000
***************
*** 0 ****
--- 1,77 ----
+ // Copyright (C) 2000 Free Software Foundation, Inc.
+ // Contributed by Nathan Sidwell 6 June 2000 <nathan@codesourcery.com>
+
+ // Check we can throw a bad_alloc exception when malloc dies
+
+ static __SIZE_TYPE__ arena[100000]; // so things can initialize
+ static int fail;
+ static unsigned pos;
+
+ extern "C" void *malloc (__SIZE_TYPE__ size)
+ {
+ unsigned *p = &arena[pos];
+
+ if (fail)
+ return 0;
+
+ arena[pos] = size;
+ size = (size + 4 * sizeof (__SIZE_TYPE__) - 1)
+ / sizeof (__SIZE_TYPE__) & ~3; // Yes, this is a hack
+ pos += size + 4;
+ return p + 4;
+ }
+ extern "C" void free (void *)
+ {
+
+ }
+ extern "C" void *realloc (void *p, __SIZE_TYPE__ size)
+ {
+ void *r = malloc (size);
+ unsigned int oldSize;
+
+ if (r && p)
+ {
+ oldSize = ((__SIZE_TYPE__ *)p)[-4];
+ if (oldSize < size)
+ size = oldSize;
+ while (size--)
+ ((char *)r)[size] = ((char *)p)[size];
+ }
+ free (p);
+ return r;
+ }
+
+ void fn_throw() throw(int)
+ {
+ throw 1;
+ }
+
+ void fn_rethrow() throw(int)
+ {
+ try{fn_throw();}
+ catch(int a){
+ throw;}
+ }
+
+ void fn_catchthrow() throw(int)
+ {
+ try{fn_throw();}
+ catch(int a){
+ throw a + 1;}
+ }
+
+ int main()
+ {
+ fail = 1;
+
+ try{fn_throw();}
+ catch(int a){}
+
+ try{fn_rethrow();}
+ catch(int a){}
+
+ try{fn_catchthrow();}
+ catch(int a){}
+
+ return 0;
+ }