Synopsis - Cross-Reference

File: /src/Synopsis/gc/malloc.c
  1/* 
  2 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
  3 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
  4 * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
  5 *
  6 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  7 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
  8 *
  9 * Permission is hereby granted to use or copy this program
 10 * for any purpose,  provided the above notices are retained on all copies.
 11 * Permission to modify the code and to distribute modified code is granted,
 12 * provided the above notices are retained, and a notice that the code was
 13 * modified is included with the above copyright notice.
 14 */
 15 
 16#include <stdio.h>
 17#include <string.h>
 18#include <errno.h>
 19#include "private/gc_priv.h"
 20
 21extern void * GC_clear_stack(void *);	/* in misc.c, behaves like identity */
 22void GC_extend_size_map(size_t);	/* in misc.c. */
 23
 24/* Allocate reclaim list for kind:	*/
 25/* Return TRUE on success		*/
 26GC_bool GC_alloc_reclaim_list(struct obj_kind *kind)
 27{
 28    struct hblk ** result = (struct hblk **)
 29    		GC_scratch_alloc((MAXOBJGRANULES+1) * sizeof(struct hblk *));
 30    if (result == 0) return(FALSE);
 31    BZERO(result, (MAXOBJGRANULES+1)*sizeof(struct hblk *));
 32    kind -> ok_reclaim_list = result;
 33    return(TRUE);
 34}
 35
 36/* Allocate a large block of size lb bytes.	*/
 37/* The block is not cleared.			*/
 38/* Flags is 0 or IGNORE_OFF_PAGE.		*/
 39/* We hold the allocation lock.			*/
 40/* EXTRA_BYTES were already added to lb.	*/
 41ptr_t GC_alloc_large(size_t lb, int k, unsigned flags)
 42{
 43    struct hblk * h;
 44    word n_blocks;
 45    ptr_t result;
 46	
 47    /* Round up to a multiple of a granule. */
 48      lb = (lb + GRANULE_BYTES - 1) & ~(GRANULE_BYTES - 1);
 49    n_blocks = OBJ_SZ_TO_BLOCKS(lb);
 50    if (!GC_is_initialized) GC_init_inner();
 51    /* Do our share of marking work */
 52        if(GC_incremental && !GC_dont_gc)
 53	    GC_collect_a_little_inner((int)n_blocks);
 54    h = GC_allochblk(lb, k, flags);
 55#   ifdef USE_MUNMAP
 56	if (0 == h) {
 57	    GC_merge_unmapped();
 58	    h = GC_allochblk(lb, k, flags);
 59	}
 60#   endif
 61    while (0 == h && GC_collect_or_expand(n_blocks, (flags != 0))) {
 62	h = GC_allochblk(lb, k, flags);
 63    }
 64    if (h == 0) {
 65	result = 0;
 66    } else {
 67	size_t total_bytes = n_blocks * HBLKSIZE;
 68	if (n_blocks > 1) {
 69	    GC_large_allocd_bytes += total_bytes;
 70	    if (GC_large_allocd_bytes > GC_max_large_allocd_bytes)
 71	        GC_max_large_allocd_bytes = GC_large_allocd_bytes;
 72	}
 73	result = h -> hb_body;
 74    }
 75    return result;
 76}
 77
 78
 79/* Allocate a large block of size lb bytes.  Clear if appropriate.	*/
 80/* We hold the allocation lock.						*/
 81/* EXTRA_BYTES were already added to lb.				*/
 82ptr_t GC_alloc_large_and_clear(size_t lb, int k, unsigned flags)
 83{
 84    ptr_t result = GC_alloc_large(lb, k, flags);
 85    word n_blocks = OBJ_SZ_TO_BLOCKS(lb);
 86
 87    if (0 == result) return 0;
 88    if (GC_debugging_started || GC_obj_kinds[k].ok_init) {
 89	/* Clear the whole block, in case of GC_realloc call. */
 90	BZERO(result, n_blocks * HBLKSIZE);
 91    }
 92    return result;
 93}
 94
 95/* allocate lb bytes for an object of kind k.	*/
 96/* Should not be used to directly to allocate	*/
 97/* objects such as STUBBORN objects that	*/
 98/* require special handling on allocation.	*/
 99/* First a version that assumes we already	*/
100/* hold lock:					*/
101void * GC_generic_malloc_inner(size_t lb, int k)
102{
103    void *op;
104
105    if(SMALL_OBJ(lb)) {
106        struct obj_kind * kind = GC_obj_kinds + k;
107	size_t lg = GC_size_map[lb];
108	void ** opp = &(kind -> ok_freelist[lg]);
109
110        if( (op = *opp) == 0 ) {
111	    if (GC_size_map[lb] == 0) {
112	      if (!GC_is_initialized)  GC_init_inner();
113	      if (GC_size_map[lb] == 0) GC_extend_size_map(lb);
114	      return(GC_generic_malloc_inner(lb, k));
115	    }
116	    if (kind -> ok_reclaim_list == 0) {
117	    	if (!GC_alloc_reclaim_list(kind)) goto out;
118	    }
119	    op = GC_allocobj(lg, k);
120	    if (op == 0) goto out;
121        }
122        *opp = obj_link(op);
123        obj_link(op) = 0;
124        GC_bytes_allocd += GRANULES_TO_BYTES(lg);
125    } else {
126	op = (ptr_t)GC_alloc_large_and_clear(ADD_SLOP(lb), k, 0);
127        GC_bytes_allocd += lb;
128    }
129    
130out:
131    return op;
132}
133
134/* Allocate a composite object of size n bytes.  The caller guarantees  */
135/* that pointers past the first page are not relevant.  Caller holds    */
136/* allocation lock.                                                     */
137void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k)
138{
139    word lb_adjusted;
140    void * op;
141
142    if (lb <= HBLKSIZE)
143        return(GC_generic_malloc_inner(lb, k));
144    lb_adjusted = ADD_SLOP(lb);
145    op = GC_alloc_large_and_clear(lb_adjusted, k, IGNORE_OFF_PAGE);
146    GC_bytes_allocd += lb_adjusted;
147    return op;
148}
149
150void * GC_generic_malloc(size_t lb, int k)
151{
152    void * result;
153    DCL_LOCK_STATE;
154
155    if (GC_have_errors) GC_print_all_errors();
156    GC_INVOKE_FINALIZERS();
157    if (SMALL_OBJ(lb)) {
158	LOCK();
159        result = GC_generic_malloc_inner((word)lb, k);
160	UNLOCK();
161    } else {
162	size_t lw;
163	size_t lb_rounded;
164	word n_blocks;
165	GC_bool init;
166	lw = ROUNDED_UP_WORDS(lb);
167	lb_rounded = WORDS_TO_BYTES(lw);
168	n_blocks = OBJ_SZ_TO_BLOCKS(lb_rounded);
169	init = GC_obj_kinds[k].ok_init;
170	LOCK();
171	result = (ptr_t)GC_alloc_large(lb_rounded, k, 0);
172	if (0 != result) {
173	  if (GC_debugging_started) {
174	    BZERO(result, n_blocks * HBLKSIZE);
175	  } else {
176#           ifdef THREADS
177	      /* Clear any memory that might be used for GC descriptors */
178	      /* before we release the lock.			      */
179	        ((word *)result)[0] = 0;
180	        ((word *)result)[1] = 0;
181	        ((word *)result)[lw-1] = 0;
182	        ((word *)result)[lw-2] = 0;
183#	    endif
184	  }
185	}
186	GC_bytes_allocd += lb_rounded;
187	UNLOCK();
188    	if (init && !GC_debugging_started && 0 != result) {
189	    BZERO(result, n_blocks * HBLKSIZE);
190        }
191    }
192    if (0 == result) {
193        return((*GC_oom_fn)(lb));
194    } else {
195        return(result);
196    }
197}   
198
199
200#define GENERAL_MALLOC(lb,k) \
201    GC_clear_stack(GC_generic_malloc(lb, k))
202/* We make the GC_clear_stack_call a tail call, hoping to get more of	*/
203/* the stack.								*/
204
205/* Allocate lb bytes of atomic (pointerfree) data */
206#ifdef THREAD_LOCAL_ALLOC
207  void * GC_core_malloc_atomic(size_t lb)
208#else
209  void * GC_malloc_atomic(size_t lb)
210#endif
211{
212    void *op;
213    void ** opp;
214    size_t lg;
215    DCL_LOCK_STATE;
216
217    if(SMALL_OBJ(lb)) {
218	lg = GC_size_map[lb];
219	opp = &(GC_aobjfreelist[lg]);
220	LOCK();
221        if( EXPECT((op = *opp) == 0, 0) ) {
222            UNLOCK();
223            return(GENERAL_MALLOC((word)lb, PTRFREE));
224        }
225        *opp = obj_link(op);
226        GC_bytes_allocd += GRANULES_TO_BYTES(lg);
227        UNLOCK();
228        return((void *) op);
229   } else {
230       return(GENERAL_MALLOC((word)lb, PTRFREE));
231   }
232}
233
234/* provide a version of strdup() that uses the collector to allocate the
235   copy of the string */
236# ifdef __STDC__
237    char *GC_strdup(const char *s)
238# else
239    char *GC_strdup(s)
240    char *s;
241#endif
242{
243  char *copy;
244
245  if (s == NULL) return NULL;
246  if ((copy = GC_malloc_atomic(strlen(s) + 1)) == NULL) {
247    errno = ENOMEM;
248    return NULL;
249  }
250  strcpy(copy, s);
251  return copy;
252}
253
254/* Allocate lb bytes of composite (pointerful) data */
255#ifdef THREAD_LOCAL_ALLOC
256  void * GC_core_malloc(size_t lb)
257#else
258  void * GC_malloc(size_t lb)
259#endif
260{
261    void *op;
262    void **opp;
263    size_t lg;
264    DCL_LOCK_STATE;
265
266    if(SMALL_OBJ(lb)) {
267	lg = GC_size_map[lb];
268	opp = (void **)&(GC_objfreelist[lg]);
269	LOCK();
270        if( EXPECT((op = *opp) == 0, 0) ) {
271            UNLOCK();
272            return(GENERAL_MALLOC((word)lb, NORMAL));
273        }
274        /* See above comment on signals.	*/
275	GC_ASSERT(0 == obj_link(op)
276		  || (word)obj_link(op)
277		  	<= (word)GC_greatest_plausible_heap_addr
278		     && (word)obj_link(op)
279		     	>= (word)GC_least_plausible_heap_addr);
280        *opp = obj_link(op);
281        obj_link(op) = 0;
282        GC_bytes_allocd += GRANULES_TO_BYTES(lg);
283        UNLOCK();
284        return op;
285   } else {
286       return(GENERAL_MALLOC(lb, NORMAL));
287   }
288}
289
290# ifdef REDIRECT_MALLOC
291
292/* Avoid unnecessary nested procedure calls here, by #defining some	*/
293/* malloc replacements.  Otherwise we end up saving a 			*/
294/* meaningless return address in the object.  It also speeds things up,	*/
295/* but it is admittedly quite ugly.					*/
296# ifdef GC_ADD_CALLER
297#   define RA GC_RETURN_ADDR,
298# else
299#   define RA
300# endif
301# define GC_debug_malloc_replacement(lb) \
302	GC_debug_malloc(lb, RA "unknown", 0)
303
304void * malloc(size_t lb)
305  {
306    /* It might help to manually inline the GC_malloc call here.	*/
307    /* But any decent compiler should reduce the extra procedure call	*/
308    /* to at most a jump instruction in this case.			*/
309#   if defined(I386) && defined(GC_SOLARIS_THREADS)
310      /*
311       * Thread initialisation can call malloc before
312       * we're ready for it.
313       * It's not clear that this is enough to help matters.
314       * The thread implementation may well call malloc at other
315       * inopportune times.
316       */
317      if (!GC_is_initialized) return sbrk(lb);
318#   endif /* I386 && GC_SOLARIS_THREADS */
319    return((void *)REDIRECT_MALLOC(lb));
320  }
321
322#ifdef GC_LINUX_THREADS
323  static ptr_t GC_libpthread_start = 0;
324  static ptr_t GC_libpthread_end = 0;
325  static ptr_t GC_libld_start = 0;
326  static ptr_t GC_libld_end = 0;
327  extern GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp);
328  	/* From os_dep.c */
329
330  void GC_init_lib_bounds(void)
331  {
332    if (GC_libpthread_start != 0) return;
333    if (!GC_text_mapping("/lib/tls/libpthread-",
334			 &GC_libpthread_start, &GC_libpthread_end)
335	&& !GC_text_mapping("/lib/libpthread-",
336			    &GC_libpthread_start, &GC_libpthread_end)) {
337	WARN("Failed to find libpthread.so text mapping: Expect crash\n", 0);
338        /* This might still work with some versions of libpthread,	*/
339    	/* so we don't abort.  Perhaps we should.			*/
340        /* Generate message only once:					*/
341          GC_libpthread_start = (ptr_t)1;
342    }
343    if (!GC_text_mapping("/lib/ld-", &GC_libld_start, &GC_libld_end)) {
344	WARN("Failed to find ld.so text mapping: Expect crash\n", 0);
345    }
346  }
347#endif
348
349void * calloc(size_t n, size_t lb)
350{
351#   if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES)
352	/* libpthread allocated some memory that is only pointed to by	*/
353	/* mmapped thread stacks.  Make sure it's not collectable.	*/
354	{
355	  static GC_bool lib_bounds_set = FALSE;
356	  ptr_t caller = (ptr_t)__builtin_return_address(0);
357	  /* This test does not need to ensure memory visibility, since */
358	  /* the bounds will be set when/if we create another thread.	*/
359	  if (!lib_bounds_set) {
360	    GC_init_lib_bounds();
361	    lib_bounds_set = TRUE;
362	  }
363	  if (caller >= GC_libpthread_start && caller < GC_libpthread_end
364	      || (caller >= GC_libld_start && caller < GC_libld_end))
365	    return GC_malloc_uncollectable(n*lb);
366	  /* The two ranges are actually usually adjacent, so there may	*/
367	  /* be a way to speed this up.					*/
368	}
369#   endif
370    return((void *)REDIRECT_MALLOC(n*lb));
371}
372
373#ifndef strdup
374# include <string.h>
375  char *strdup(const char *s)
376  {
377    size_t len = strlen(s) + 1;
378    char * result = ((char *)REDIRECT_MALLOC(len+1));
379    if (result == 0) {
380      errno = ENOMEM;
381      return 0;
382    }
383    BCOPY(s, result, len+1);
384    return result;
385  }
386#endif /* !defined(strdup) */
387 /* If strdup is macro defined, we assume that it actually calls malloc, */
388 /* and thus the right thing will happen even without overriding it.	 */
389 /* This seems to be true on most Linux systems.			 */
390
391#undef GC_debug_malloc_replacement
392
393# endif /* REDIRECT_MALLOC */
394
395/* Explicitly deallocate an object p.				*/
396void GC_free(void * p)
397{
398    struct hblk *h;
399    hdr *hhdr;
400    size_t sz; /* In bytes */
401    size_t ngranules;	/* sz in granules */
402    void **flh;
403    int knd;
404    struct obj_kind * ok;
405    DCL_LOCK_STATE;
406
407    if (p == 0) return;
408    	/* Required by ANSI.  It's not my fault ...	*/
409    h = HBLKPTR(p);
410    hhdr = HDR(h);
411    sz = hhdr -> hb_sz;
412    ngranules = BYTES_TO_GRANULES(sz);
413    GC_ASSERT(GC_base(p) == p);
414#   if defined(REDIRECT_MALLOC) && \
415	(defined(GC_SOLARIS_THREADS) || defined(GC_LINUX_THREADS) \
416	 || defined(MSWIN32))
417	/* For Solaris, we have to redirect malloc calls during		*/
418	/* initialization.  For the others, this seems to happen 	*/
419 	/* implicitly.							*/
420	/* Don't try to deallocate that memory.				*/
421	if (0 == hhdr) return;
422#   endif
423    knd = hhdr -> hb_obj_kind;
424    ok = &GC_obj_kinds[knd];
425    if (EXPECT((ngranules <= MAXOBJGRANULES), 1)) {
426	LOCK();
427	GC_bytes_freed += sz;
428	if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz;
429		/* Its unnecessary to clear the mark bit.  If the 	*/
430		/* object is reallocated, it doesn't matter.  O.w. the	*/
431		/* collector will do it, since it's on a free list.	*/
432	if (ok -> ok_init) {
433	    BZERO((word *)p + 1, sz-sizeof(word));
434	}
435	flh = &(ok -> ok_freelist[ngranules]);
436	obj_link(p) = *flh;
437	*flh = (ptr_t)p;
438	UNLOCK();
439    } else {
440        LOCK();
441        GC_bytes_freed += sz;
442	if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz;
443        GC_freehblk(h);
444        UNLOCK();
445    }
446}
447
448/* Explicitly deallocate an object p when we already hold lock.		*/
449/* Only used for internally allocated objects, so we can take some 	*/
450/* shortcuts.								*/
451#ifdef THREADS
452void GC_free_inner(void * p)
453{
454    struct hblk *h;
455    hdr *hhdr;
456    size_t sz; /* bytes */
457    size_t ngranules;  /* sz in granules */
458    void ** flh;
459    int knd;
460    struct obj_kind * ok;
461    DCL_LOCK_STATE;
462
463    h = HBLKPTR(p);
464    hhdr = HDR(h);
465    knd = hhdr -> hb_obj_kind;
466    sz = hhdr -> hb_sz;
467    ngranules = BYTES_TO_GRANULES(sz);
468    ok = &GC_obj_kinds[knd];
469    if (ngranules <= MAXOBJGRANULES) {
470	GC_bytes_freed += sz;
471	if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz;
472	if (ok -> ok_init) {
473	    BZERO((word *)p + 1, sz-sizeof(word));
474	}
475	flh = &(ok -> ok_freelist[ngranules]);
476	obj_link(p) = *flh;
477	*flh = (ptr_t)p;
478    } else {
479        GC_bytes_freed += sz;
480	if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz;
481        GC_freehblk(h);
482    }
483}
484#endif /* THREADS */
485
486# if defined(REDIRECT_MALLOC) && !defined(REDIRECT_FREE)
487#   define REDIRECT_FREE GC_free
488# endif
489# ifdef REDIRECT_FREE
490  void free(void * p)
491  {
492#   if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES)
493	{
494	  /* Don't bother with initialization checks.  If nothing	*/
495	  /* has been initialized, the check fails, and that's safe,	*/
496	  /* since we haven't allocated uncollectable objects either.	*/
497	  ptr_t caller = (ptr_t)__builtin_return_address(0);
498	  /* This test does not need to ensure memory visibility, since */
499	  /* the bounds will be set when/if we create another thread.	*/
500	  if (caller >= GC_libpthread_start && caller > GC_libpthread_end) {
501	    GC_free(p);
502	    return;
503	  }
504	}
505#   endif
506#   ifndef IGNORE_FREE
507      REDIRECT_FREE(p);
508#   endif
509  }
510# endif  /* REDIRECT_MALLOC */