This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: [exception patch]: Throw bad alloc


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;
+ }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]