| 1 | /***************************************************************************
|
|---|
| 2 |
|
|---|
| 3 | Interface between g++ and Boehm GC
|
|---|
| 4 |
|
|---|
| 5 | Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
|
|---|
| 6 |
|
|---|
| 7 | THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
|
|---|
| 8 | OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
|
|---|
| 9 |
|
|---|
| 10 | Permission is hereby granted to copy this code for any purpose,
|
|---|
| 11 | provided the above notices are retained on all copies.
|
|---|
| 12 |
|
|---|
| 13 | Last modified on Sun Jul 16 23:21:14 PDT 1995 by ellis
|
|---|
| 14 |
|
|---|
| 15 | This module provides runtime support for implementing the
|
|---|
| 16 | Ellis/Detlefs GC proposal, "Safe, Efficient Garbage Collection for
|
|---|
| 17 | C++", within g++, using its -fgc-keyword extension. It defines
|
|---|
| 18 | versions of __builtin_new, __builtin_new_gc, __builtin_vec_new,
|
|---|
| 19 | __builtin_vec_new_gc, __builtin_delete, and __builtin_vec_delete that
|
|---|
| 20 | invoke the Bohem GC. It also implements the WeakPointer.h interface.
|
|---|
| 21 |
|
|---|
| 22 | This module assumes the following configuration options of the Boehm GC:
|
|---|
| 23 |
|
|---|
| 24 | -DALL_INTERIOR_POINTERS
|
|---|
| 25 | -DDONT_ADD_BYTE_AT_END
|
|---|
| 26 |
|
|---|
| 27 | This module adds its own required padding to the end of objects to
|
|---|
| 28 | support C/C++ "one-past-the-object" pointer semantics.
|
|---|
| 29 |
|
|---|
| 30 | ****************************************************************************/
|
|---|
| 31 |
|
|---|
| 32 | #include <stddef.h>
|
|---|
| 33 | #include "gc.h"
|
|---|
| 34 |
|
|---|
| 35 | #if defined(__STDC__)
|
|---|
| 36 | # define PROTO( args ) args
|
|---|
| 37 | #else
|
|---|
| 38 | # define PROTO( args ) ()
|
|---|
| 39 | # endif
|
|---|
| 40 |
|
|---|
| 41 | #define BITSPERBYTE 8
|
|---|
| 42 | /* What's the portable way to do this? */
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 | typedef void (*vfp) PROTO(( void ));
|
|---|
| 46 | extern vfp __new_handler;
|
|---|
| 47 | extern void __default_new_handler PROTO(( void ));
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 | /* A destructor_proc is the compiler generated procedure representing a
|
|---|
| 51 | C++ destructor. The "flag" argument is a hidden argument following some
|
|---|
| 52 | compiler convention. */
|
|---|
| 53 |
|
|---|
| 54 | typedef (*destructor_proc) PROTO(( void* this, int flag ));
|
|---|
| 55 |
|
|---|
| 56 |
|
|---|
| 57 | /***************************************************************************
|
|---|
| 58 |
|
|---|
| 59 | A BI_header is the header the compiler adds to the front of
|
|---|
| 60 | new-allocated arrays of objects with destructors. The header is
|
|---|
| 61 | padded out to a double, because that's what the compiler does to
|
|---|
| 62 | ensure proper alignment of array elements on some architectures.
|
|---|
| 63 |
|
|---|
| 64 | int NUM_ARRAY_ELEMENTS (void* o)
|
|---|
| 65 | returns the number of array elements for array object o.
|
|---|
| 66 |
|
|---|
| 67 | char* FIRST_ELEMENT_P (void* o)
|
|---|
| 68 | returns the address of the first element of array object o.
|
|---|
| 69 |
|
|---|
| 70 | ***************************************************************************/
|
|---|
| 71 |
|
|---|
| 72 | typedef struct BI_header {
|
|---|
| 73 | int nelts;
|
|---|
| 74 | char padding [sizeof( double ) - sizeof( int )];
|
|---|
| 75 | /* Better way to do this? */
|
|---|
| 76 | } BI_header;
|
|---|
| 77 |
|
|---|
| 78 | #define NUM_ARRAY_ELEMENTS( o ) \
|
|---|
| 79 | (((BI_header*) o)->nelts)
|
|---|
| 80 |
|
|---|
| 81 | #define FIRST_ELEMENT_P( o ) \
|
|---|
| 82 | ((char*) o + sizeof( BI_header ))
|
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 | /***************************************************************************
|
|---|
| 86 |
|
|---|
| 87 | The __builtin_new routines add a descriptor word to the end of each
|
|---|
| 88 | object. The descriptor serves two purposes.
|
|---|
| 89 |
|
|---|
| 90 | First, the descriptor acts as padding, implementing C/C++ pointer
|
|---|
| 91 | semantics. C and C++ allow a valid array pointer to be incremented
|
|---|
| 92 | one past the end of an object. The extra padding ensures that the
|
|---|
| 93 | collector will recognize that such a pointer points to the object and
|
|---|
| 94 | not the next object in memory.
|
|---|
| 95 |
|
|---|
| 96 | Second, the descriptor stores three extra pieces of information,
|
|---|
| 97 | whether an object has a registered finalizer (destructor), whether it
|
|---|
| 98 | may have any weak pointers referencing it, and for collectible arrays,
|
|---|
| 99 | the element size of the array. The element size is required for the
|
|---|
| 100 | array's finalizer to iterate through the elements of the array. (An
|
|---|
| 101 | alternative design would have the compiler generate a finalizer
|
|---|
| 102 | procedure for each different array type. But given the overhead of
|
|---|
| 103 | finalization, there isn't any efficiency to be gained by that.)
|
|---|
| 104 |
|
|---|
| 105 | The descriptor must be added to non-collectible as well as collectible
|
|---|
| 106 | objects, since the Ellis/Detlefs proposal allows "pointer to gc T" to
|
|---|
| 107 | be assigned to a "pointer to T", which could then be deleted. Thus,
|
|---|
| 108 | __builtin_delete must determine at runtime whether an object is
|
|---|
| 109 | collectible, whether it has weak pointers referencing it, and whether
|
|---|
| 110 | it may have a finalizer that needs unregistering. Though
|
|---|
| 111 | GC_REGISTER_FINALIZER doesn't care if you ask it to unregister a
|
|---|
| 112 | finalizer for an object that doesn't have one, it is a non-trivial
|
|---|
| 113 | procedure that does a hash look-up, etc. The descriptor trades a
|
|---|
| 114 | little extra space for a significant increase in time on the fast path
|
|---|
| 115 | through delete. (A similar argument applies to
|
|---|
| 116 | GC_UNREGISTER_DISAPPEARING_LINK).
|
|---|
| 117 |
|
|---|
| 118 | For non-array types, the space for the descriptor could be shrunk to a
|
|---|
| 119 | single byte for storing the "has finalizer" flag. But this would save
|
|---|
| 120 | space only on arrays of char (whose size is not a multiple of the word
|
|---|
| 121 | size) and structs whose largest member is less than a word in size
|
|---|
| 122 | (very infrequent). And it would require that programmers actually
|
|---|
| 123 | remember to call "delete[]" instead of "delete" (which they should,
|
|---|
| 124 | but there are probably lots of buggy programs out there). For the
|
|---|
| 125 | moment, the space savings seems not worthwhile, especially considering
|
|---|
| 126 | that the Boehm GC is already quite space competitive with other
|
|---|
| 127 | malloc's.
|
|---|
| 128 |
|
|---|
| 129 |
|
|---|
| 130 | Given a pointer o to the base of an object:
|
|---|
| 131 |
|
|---|
| 132 | Descriptor* DESCRIPTOR (void* o)
|
|---|
| 133 | returns a pointer to the descriptor for o.
|
|---|
| 134 |
|
|---|
| 135 | The implementation of descriptors relies on the fact that the GC
|
|---|
| 136 | implementation allocates objects in units of the machine's natural
|
|---|
| 137 | word size (e.g. 32 bits on a SPARC, 64 bits on an Alpha).
|
|---|
| 138 |
|
|---|
| 139 | **************************************************************************/
|
|---|
| 140 |
|
|---|
| 141 | typedef struct Descriptor {
|
|---|
| 142 | unsigned has_weak_pointers: 1;
|
|---|
| 143 | unsigned has_finalizer: 1;
|
|---|
| 144 | unsigned element_size: BITSPERBYTE * sizeof( unsigned ) - 2;
|
|---|
| 145 | } Descriptor;
|
|---|
| 146 |
|
|---|
| 147 | #define DESCRIPTOR( o ) \
|
|---|
| 148 | ((Descriptor*) ((char*)(o) + GC_size( o ) - sizeof( Descriptor )))
|
|---|
| 149 |
|
|---|
| 150 |
|
|---|
| 151 | /**************************************************************************
|
|---|
| 152 |
|
|---|
| 153 | Implementations of global operator new() and operator delete()
|
|---|
| 154 |
|
|---|
| 155 | ***************************************************************************/
|
|---|
| 156 |
|
|---|
| 157 |
|
|---|
| 158 | void* __builtin_new( size )
|
|---|
| 159 | size_t size;
|
|---|
| 160 | /*
|
|---|
| 161 | For non-gc non-array types, the compiler generates calls to
|
|---|
| 162 | __builtin_new, which allocates non-collected storage via
|
|---|
| 163 | GC_MALLOC_UNCOLLECTABLE. This ensures that the non-collected
|
|---|
| 164 | storage will be part of the collector's root set, required by the
|
|---|
| 165 | Ellis/Detlefs semantics. */
|
|---|
| 166 | {
|
|---|
| 167 | vfp handler = __new_handler ? __new_handler : __default_new_handler;
|
|---|
| 168 |
|
|---|
| 169 | while (1) {
|
|---|
| 170 | void* o = GC_MALLOC_UNCOLLECTABLE( size + sizeof( Descriptor ) );
|
|---|
| 171 | if (o != 0) return o;
|
|---|
| 172 | (*handler) ();}}
|
|---|
| 173 |
|
|---|
| 174 |
|
|---|
| 175 | void* __builtin_vec_new( size )
|
|---|
| 176 | size_t size;
|
|---|
| 177 | /*
|
|---|
| 178 | For non-gc array types, the compiler generates calls to
|
|---|
| 179 | __builtin_vec_new. */
|
|---|
| 180 | {
|
|---|
| 181 | return __builtin_new( size );}
|
|---|
| 182 |
|
|---|
| 183 |
|
|---|
| 184 | void* __builtin_new_gc( size )
|
|---|
| 185 | size_t size;
|
|---|
| 186 | /*
|
|---|
| 187 | For gc non-array types, the compiler generates calls to
|
|---|
| 188 | __builtin_new_gc, which allocates collected storage via
|
|---|
| 189 | GC_MALLOC. */
|
|---|
| 190 | {
|
|---|
| 191 | vfp handler = __new_handler ? __new_handler : __default_new_handler;
|
|---|
| 192 |
|
|---|
| 193 | while (1) {
|
|---|
| 194 | void* o = GC_MALLOC( size + sizeof( Descriptor ) );
|
|---|
| 195 | if (o != 0) return o;
|
|---|
| 196 | (*handler) ();}}
|
|---|
| 197 |
|
|---|
| 198 |
|
|---|
| 199 | void* __builtin_new_gc_a( size )
|
|---|
| 200 | size_t size;
|
|---|
| 201 | /*
|
|---|
| 202 | For non-pointer-containing gc non-array types, the compiler
|
|---|
| 203 | generates calls to __builtin_new_gc_a, which allocates collected
|
|---|
| 204 | storage via GC_MALLOC_ATOMIC. */
|
|---|
| 205 | {
|
|---|
| 206 | vfp handler = __new_handler ? __new_handler : __default_new_handler;
|
|---|
| 207 |
|
|---|
| 208 | while (1) {
|
|---|
| 209 | void* o = GC_MALLOC_ATOMIC( size + sizeof( Descriptor ) );
|
|---|
| 210 | if (o != 0) return o;
|
|---|
| 211 | (*handler) ();}}
|
|---|
| 212 |
|
|---|
| 213 |
|
|---|
| 214 | void* __builtin_vec_new_gc( size )
|
|---|
| 215 | size_t size;
|
|---|
| 216 | /*
|
|---|
| 217 | For gc array types, the compiler generates calls to
|
|---|
| 218 | __builtin_vec_new_gc. */
|
|---|
| 219 | {
|
|---|
| 220 | return __builtin_new_gc( size );}
|
|---|
| 221 |
|
|---|
| 222 |
|
|---|
| 223 | void* __builtin_vec_new_gc_a( size )
|
|---|
| 224 | size_t size;
|
|---|
| 225 | /*
|
|---|
| 226 | For non-pointer-containing gc array types, the compiler generates
|
|---|
| 227 | calls to __builtin_vec_new_gc_a. */
|
|---|
| 228 | {
|
|---|
| 229 | return __builtin_new_gc_a( size );}
|
|---|
| 230 |
|
|---|
| 231 |
|
|---|
| 232 | static void call_destructor( o, data )
|
|---|
| 233 | void* o;
|
|---|
| 234 | void* data;
|
|---|
| 235 | /*
|
|---|
| 236 | call_destructor is the GC finalizer proc registered for non-array
|
|---|
| 237 | gc objects with destructors. Its client data is the destructor
|
|---|
| 238 | proc, which it calls with the magic integer 2, a special flag
|
|---|
| 239 | obeying the compiler convention for destructors. */
|
|---|
| 240 | {
|
|---|
| 241 | ((destructor_proc) data)( o, 2 );}
|
|---|
| 242 |
|
|---|
| 243 |
|
|---|
| 244 | void* __builtin_new_gc_dtor( o, d )
|
|---|
| 245 | void* o;
|
|---|
| 246 | destructor_proc d;
|
|---|
| 247 | /*
|
|---|
| 248 | The compiler generates a call to __builtin_new_gc_dtor to register
|
|---|
| 249 | the destructor "d" of a non-array gc object "o" as a GC finalizer.
|
|---|
| 250 | The destructor is registered via
|
|---|
| 251 | GC_REGISTER_FINALIZER_IGNORE_SELF, which causes the collector to
|
|---|
| 252 | ignore pointers from the object to itself when determining when
|
|---|
| 253 | the object can be finalized. This is necessary due to the self
|
|---|
| 254 | pointers used in the internal representation of multiply-inherited
|
|---|
| 255 | objects. */
|
|---|
| 256 | {
|
|---|
| 257 | Descriptor* desc = DESCRIPTOR( o );
|
|---|
| 258 |
|
|---|
| 259 | GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_destructor, d, 0, 0 );
|
|---|
| 260 | desc->has_finalizer = 1;}
|
|---|
| 261 |
|
|---|
| 262 |
|
|---|
| 263 | static void call_array_destructor( o, data )
|
|---|
| 264 | void* o;
|
|---|
| 265 | void* data;
|
|---|
| 266 | /*
|
|---|
| 267 | call_array_destructor is the GC finalizer proc registered for gc
|
|---|
| 268 | array objects whose elements have destructors. Its client data is
|
|---|
| 269 | the destructor proc. It iterates through the elements of the
|
|---|
| 270 | array in reverse order, calling the destructor on each. */
|
|---|
| 271 | {
|
|---|
| 272 | int num = NUM_ARRAY_ELEMENTS( o );
|
|---|
| 273 | Descriptor* desc = DESCRIPTOR( o );
|
|---|
| 274 | size_t size = desc->element_size;
|
|---|
| 275 | char* first_p = FIRST_ELEMENT_P( o );
|
|---|
| 276 | char* p = first_p + (num - 1) * size;
|
|---|
| 277 |
|
|---|
| 278 | if (num > 0) {
|
|---|
| 279 | while (1) {
|
|---|
| 280 | ((destructor_proc) data)( p, 2 );
|
|---|
| 281 | if (p == first_p) break;
|
|---|
| 282 | p -= size;}}}
|
|---|
| 283 |
|
|---|
| 284 |
|
|---|
| 285 | void* __builtin_vec_new_gc_dtor( first_elem, d, element_size )
|
|---|
| 286 | void* first_elem;
|
|---|
| 287 | destructor_proc d;
|
|---|
| 288 | size_t element_size;
|
|---|
| 289 | /*
|
|---|
| 290 | The compiler generates a call to __builtin_vec_new_gc_dtor to
|
|---|
| 291 | register the destructor "d" of a gc array object as a GC
|
|---|
| 292 | finalizer. "first_elem" points to the first element of the array,
|
|---|
| 293 | *not* the beginning of the object (this makes the generated call
|
|---|
| 294 | to this function smaller). The elements of the array are of size
|
|---|
| 295 | "element_size". The destructor is registered as in
|
|---|
| 296 | _builtin_new_gc_dtor. */
|
|---|
| 297 | {
|
|---|
| 298 | void* o = (char*) first_elem - sizeof( BI_header );
|
|---|
| 299 | Descriptor* desc = DESCRIPTOR( o );
|
|---|
| 300 |
|
|---|
| 301 | GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_array_destructor, d, 0, 0 );
|
|---|
| 302 | desc->element_size = element_size;
|
|---|
| 303 | desc->has_finalizer = 1;}
|
|---|
| 304 |
|
|---|
| 305 |
|
|---|
| 306 | void __builtin_delete( o )
|
|---|
| 307 | void* o;
|
|---|
| 308 | /*
|
|---|
| 309 | The compiler generates calls to __builtin_delete for operator
|
|---|
| 310 | delete(). The GC currently requires that any registered
|
|---|
| 311 | finalizers be unregistered before explicitly freeing an object.
|
|---|
| 312 | If the object has any weak pointers referencing it, we can't
|
|---|
| 313 | actually free it now. */
|
|---|
| 314 | {
|
|---|
| 315 | if (o != 0) {
|
|---|
| 316 | Descriptor* desc = DESCRIPTOR( o );
|
|---|
| 317 | if (desc->has_finalizer) GC_REGISTER_FINALIZER( o, 0, 0, 0, 0 );
|
|---|
| 318 | if (! desc->has_weak_pointers) GC_FREE( o );}}
|
|---|
| 319 |
|
|---|
| 320 |
|
|---|
| 321 | void __builtin_vec_delete( o )
|
|---|
| 322 | void* o;
|
|---|
| 323 | /*
|
|---|
| 324 | The compiler generates calls to __builitn_vec_delete for operator
|
|---|
| 325 | delete[](). */
|
|---|
| 326 | {
|
|---|
| 327 | __builtin_delete( o );}
|
|---|
| 328 |
|
|---|
| 329 |
|
|---|
| 330 | /**************************************************************************
|
|---|
| 331 |
|
|---|
| 332 | Implementations of the template class WeakPointer from WeakPointer.h
|
|---|
| 333 |
|
|---|
| 334 | ***************************************************************************/
|
|---|
| 335 |
|
|---|
| 336 | typedef struct WeakPointer {
|
|---|
| 337 | void* pointer;
|
|---|
| 338 | } WeakPointer;
|
|---|
| 339 |
|
|---|
| 340 |
|
|---|
| 341 | void* _WeakPointer_New( t )
|
|---|
| 342 | void* t;
|
|---|
| 343 | {
|
|---|
| 344 | if (t == 0) {
|
|---|
| 345 | return 0;}
|
|---|
| 346 | else {
|
|---|
| 347 | void* base = GC_base( t );
|
|---|
| 348 | WeakPointer* wp =
|
|---|
| 349 | (WeakPointer*) GC_MALLOC_ATOMIC( sizeof( WeakPointer ) );
|
|---|
| 350 | Descriptor* desc = DESCRIPTOR( base );
|
|---|
| 351 |
|
|---|
| 352 | wp->pointer = t;
|
|---|
| 353 | desc->has_weak_pointers = 1;
|
|---|
| 354 | GC_general_register_disappearing_link( &wp->pointer, base );
|
|---|
| 355 | return wp;}}
|
|---|
| 356 |
|
|---|
| 357 |
|
|---|
| 358 | static void* PointerWithLock( wp )
|
|---|
| 359 | WeakPointer* wp;
|
|---|
| 360 | {
|
|---|
| 361 | if (wp == 0 || wp->pointer == 0) {
|
|---|
| 362 | return 0;}
|
|---|
| 363 | else {
|
|---|
| 364 | return (void*) wp->pointer;}}
|
|---|
| 365 |
|
|---|
| 366 |
|
|---|
| 367 | void* _WeakPointer_Pointer( wp )
|
|---|
| 368 | WeakPointer* wp;
|
|---|
| 369 | {
|
|---|
| 370 | return (void*) GC_call_with_alloc_lock( PointerWithLock, wp );}
|
|---|
| 371 |
|
|---|
| 372 |
|
|---|
| 373 | typedef struct EqualClosure {
|
|---|
| 374 | WeakPointer* wp1;
|
|---|
| 375 | WeakPointer* wp2;
|
|---|
| 376 | } EqualClosure;
|
|---|
| 377 |
|
|---|
| 378 |
|
|---|
| 379 | static void* EqualWithLock( ec )
|
|---|
| 380 | EqualClosure* ec;
|
|---|
| 381 | {
|
|---|
| 382 | if (ec->wp1 == 0 || ec->wp2 == 0) {
|
|---|
| 383 | return (void*) (ec->wp1 == ec->wp2);}
|
|---|
| 384 | else {
|
|---|
| 385 | return (void*) (ec->wp1->pointer == ec->wp2->pointer);}}
|
|---|
| 386 |
|
|---|
| 387 |
|
|---|
| 388 | int _WeakPointer_Equal( wp1, wp2 )
|
|---|
| 389 | WeakPointer* wp1;
|
|---|
| 390 | WeakPointer* wp2;
|
|---|
| 391 | {
|
|---|
| 392 | EqualClosure ec;
|
|---|
| 393 |
|
|---|
| 394 | ec.wp1 = wp1;
|
|---|
| 395 | ec.wp2 = wp2;
|
|---|
| 396 | return (int) GC_call_with_alloc_lock( EqualWithLock, &ec );}
|
|---|
| 397 |
|
|---|
| 398 |
|
|---|
| 399 | int _WeakPointer_Hash( wp )
|
|---|
| 400 | WeakPointer* wp;
|
|---|
| 401 | {
|
|---|
| 402 | return (int) _WeakPointer_Pointer( wp );}
|
|---|
| 403 |
|
|---|
| 404 |
|
|---|
| 405 | /**************************************************************************
|
|---|
| 406 |
|
|---|
| 407 | Implementations of the template class CleanUp from WeakPointer.h
|
|---|
| 408 |
|
|---|
| 409 | ***************************************************************************/
|
|---|
| 410 |
|
|---|
| 411 | typedef struct Closure {
|
|---|
| 412 | void (*c) PROTO(( void* d, void* t ));
|
|---|
| 413 | ptrdiff_t t_offset;
|
|---|
| 414 | void* d;
|
|---|
| 415 | } Closure;
|
|---|
| 416 |
|
|---|
| 417 |
|
|---|
| 418 | static void _CleanUp_CallClosure( obj, data )
|
|---|
| 419 | void* obj;
|
|---|
| 420 | void* data;
|
|---|
| 421 | {
|
|---|
| 422 | Closure* closure = (Closure*) data;
|
|---|
| 423 | closure->c( closure->d, (char*) obj + closure->t_offset );}
|
|---|
| 424 |
|
|---|
| 425 |
|
|---|
| 426 | void _CleanUp_Set( t, c, d )
|
|---|
| 427 | void* t;
|
|---|
| 428 | void (*c) PROTO(( void* d, void* t ));
|
|---|
| 429 | void* d;
|
|---|
| 430 | {
|
|---|
| 431 | void* base = GC_base( t );
|
|---|
| 432 | Descriptor* desc = DESCRIPTOR( t );
|
|---|
| 433 |
|
|---|
| 434 | if (c == 0) {
|
|---|
| 435 | GC_REGISTER_FINALIZER_IGNORE_SELF( base, 0, 0, 0, 0 );
|
|---|
| 436 | desc->has_finalizer = 0;}
|
|---|
| 437 | else {
|
|---|
| 438 | Closure* closure = (Closure*) GC_MALLOC( sizeof( Closure ) );
|
|---|
| 439 | closure->c = c;
|
|---|
| 440 | closure->t_offset = (char*) t - (char*) base;
|
|---|
| 441 | closure->d = d;
|
|---|
| 442 | GC_REGISTER_FINALIZER_IGNORE_SELF( base, _CleanUp_CallClosure,
|
|---|
| 443 | closure, 0, 0 );
|
|---|
| 444 | desc->has_finalizer = 1;}}
|
|---|
| 445 |
|
|---|
| 446 |
|
|---|
| 447 | void _CleanUp_Call( t )
|
|---|
| 448 | void* t;
|
|---|
| 449 | {
|
|---|
| 450 | /* ? Aren't we supposed to deactivate weak pointers to t too?
|
|---|
| 451 | Why? */
|
|---|
| 452 | void* base = GC_base( t );
|
|---|
| 453 | void* d;
|
|---|
| 454 | GC_finalization_proc f;
|
|---|
| 455 |
|
|---|
| 456 | GC_REGISTER_FINALIZER( base, 0, 0, &f, &d );
|
|---|
| 457 | f( base, d );}
|
|---|
| 458 |
|
|---|
| 459 |
|
|---|
| 460 | typedef struct QueueElem {
|
|---|
| 461 | void* o;
|
|---|
| 462 | GC_finalization_proc f;
|
|---|
| 463 | void* d;
|
|---|
| 464 | struct QueueElem* next;
|
|---|
| 465 | } QueueElem;
|
|---|
| 466 |
|
|---|
| 467 |
|
|---|
| 468 | void* _CleanUp_Queue_NewHead()
|
|---|
| 469 | {
|
|---|
| 470 | return GC_MALLOC( sizeof( QueueElem ) );}
|
|---|
| 471 |
|
|---|
| 472 |
|
|---|
| 473 | static void _CleanUp_Queue_Enqueue( obj, data )
|
|---|
| 474 | void* obj;
|
|---|
| 475 | void* data;
|
|---|
| 476 | {
|
|---|
| 477 | QueueElem* q = (QueueElem*) data;
|
|---|
| 478 | QueueElem* head = q->next;
|
|---|
| 479 |
|
|---|
| 480 | q->o = obj;
|
|---|
| 481 | q->next = head->next;
|
|---|
| 482 | head->next = q;}
|
|---|
| 483 |
|
|---|
| 484 |
|
|---|
| 485 | void _CleanUp_Queue_Set( h, t )
|
|---|
| 486 | void* h;
|
|---|
| 487 | void* t;
|
|---|
| 488 | {
|
|---|
| 489 | QueueElem* head = (QueueElem*) h;
|
|---|
| 490 | void* base = GC_base( t );
|
|---|
| 491 | void* d;
|
|---|
| 492 | GC_finalization_proc f;
|
|---|
| 493 | QueueElem* q = (QueueElem*) GC_MALLOC( sizeof( QueueElem ) );
|
|---|
| 494 |
|
|---|
| 495 | GC_REGISTER_FINALIZER( base, _CleanUp_Queue_Enqueue, q, &f, &d );
|
|---|
| 496 | q->f = f;
|
|---|
| 497 | q->d = d;
|
|---|
| 498 | q->next = head;}
|
|---|
| 499 |
|
|---|
| 500 |
|
|---|
| 501 | int _CleanUp_Queue_Call( h )
|
|---|
| 502 | void* h;
|
|---|
| 503 | {
|
|---|
| 504 | QueueElem* head = (QueueElem*) h;
|
|---|
| 505 | QueueElem* q = head->next;
|
|---|
| 506 |
|
|---|
| 507 | if (q == 0) {
|
|---|
| 508 | return 0;}
|
|---|
| 509 | else {
|
|---|
| 510 | head->next = q->next;
|
|---|
| 511 | q->next = 0;
|
|---|
| 512 | if (q->f != 0) q->f( q->o, q->d );
|
|---|
| 513 | return 1;}}
|
|---|
| 514 |
|
|---|
| 515 |
|
|---|
| 516 |
|
|---|