(root)/
glibc-2.38/
hurd/
hurdmalloc.c
       1  #include <stdlib.h>
       2  #include <string.h>
       3  #include "set-hooks.h"
       4  
       5  #include "hurdmalloc.h"		/* XXX see that file */
       6  
       7  #include <mach.h>
       8  #include <mach/spin-lock.h>
       9  #define vm_allocate __vm_allocate
      10  #define vm_page_size __vm_page_size
      11  
      12  /*
      13   * Mach Operating System
      14   * Copyright (c) 1991,1990,1989 Carnegie Mellon University
      15   * All Rights Reserved.
      16   *
      17   * Permission to use, copy, modify and distribute this software and its
      18   * documentation is hereby granted, provided that both the copyright
      19   * notice and this permission notice appear in all copies of the
      20   * software, derivative works or modified versions, and any portions
      21   * thereof, and that both notices appear in supporting documentation.
      22   *
      23   * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
      24   * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
      25   * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
      26   *
      27   * Carnegie Mellon requests users of this software to return to
      28   *
      29   *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
      30   *  School of Computer Science
      31   *  Carnegie Mellon University
      32   *  Pittsburgh PA 15213-3890
      33   *
      34   * any improvements or extensions that they make and grant Carnegie Mellon
      35   * the rights to redistribute these changes.
      36   */
      37  /*
      38   * (pre-GNU) HISTORY
      39   *
      40   * Revision 2.7  91/05/14  17:57:34  mrt
      41   * 	Correcting copyright
      42   *
      43   * Revision 2.6  91/02/14  14:20:26  mrt
      44   * 	Added new Mach copyright
      45   * 	[91/02/13  12:41:21  mrt]
      46   *
      47   * Revision 2.5  90/11/05  14:37:33  rpd
      48   * 	Added malloc_fork* code.
      49   * 	[90/11/02            rwd]
      50   *
      51   * 	Add spin_lock_t.
      52   * 	[90/10/31            rwd]
      53   *
      54   * Revision 2.4  90/08/07  14:31:28  rpd
      55   * 	Removed RCS keyword nonsense.
      56   *
      57   * Revision 2.3  90/06/02  15:14:00  rpd
      58   * 	Converted to new IPC.
      59   * 	[90/03/20  20:56:57  rpd]
      60   *
      61   * Revision 2.2  89/12/08  19:53:59  rwd
      62   * 	Removed conditionals.
      63   * 	[89/10/23            rwd]
      64   *
      65   * Revision 2.1  89/08/03  17:09:46  rwd
      66   * Created.
      67   *
      68   *
      69   * 13-Sep-88  Eric Cooper (ecc) at Carnegie Mellon University
      70   *	Changed realloc() to copy min(old size, new size) bytes.
      71   *	Bug found by Mike Kupfer at Olivetti.
      72   */
      73  /*
      74   * 	File: 	malloc.c
      75   *	Author: Eric Cooper, Carnegie Mellon University
      76   *	Date:	July, 1988
      77   *
      78   * 	Memory allocator for use with multiple threads.
      79   */
      80  
      81  
      82  #include <assert.h>
      83  
      84  #define MCHECK
      85  
      86  /*
      87   * Structure of memory block header.
      88   * When free, next points to next block on free list.
      89   * When allocated, fl points to free list.
      90   * Size of header is 4 bytes, so minimum usable block size is 8 bytes.
      91   */
      92  
      93  #define CHECK_BUSY  0x8a3c743e
      94  #define CHECK_FREE  0x66688b92
      95  
      96  #ifdef MCHECK
      97  
      98  typedef struct header {
      99    long check;
     100    union {
     101      struct header *next;
     102      struct free_list *fl;
     103    } u;
     104  } *header_t;
     105  
     106  #define HEADER_SIZE sizeof (struct header)
     107  #define HEADER_NEXT(h) ((h)->u.next)
     108  #define HEADER_FREE(h) ((h)->u.fl)
     109  #define HEADER_CHECK(h) ((h)->check)
     110  #define MIN_SIZE	16
     111  #define LOG2_MIN_SIZE	4
     112  
     113  #else /* ! MCHECK */
     114  
     115  typedef union header {
     116  	union header *next;
     117  	struct free_list *fl;
     118  } *header_t;
     119  
     120  #define HEADER_SIZE sizeof (union header)
     121  #define HEADER_NEXT(h) ((h)->next)
     122  #define HEADER_FREE(h) ((h)->fl)
     123  #define MIN_SIZE	8	/* minimum block size */
     124  #define LOG2_MIN_SIZE	3
     125  
     126  #endif /* MCHECK */
     127  
     128  typedef struct free_list {
     129  	spin_lock_t lock;	/* spin lock for mutual exclusion */
     130  	header_t head;		/* head of free list for this size */
     131  #ifdef	DEBUG
     132  	int in_use;		/* # mallocs - # frees */
     133  #endif	/* DEBUG */
     134  } *free_list_t;
     135  
     136  /*
     137   * Free list with index i contains blocks of size 2 ^ (i + LOG2_MIN_SIZE)
     138   * including header.  Smallest block size is MIN_SIZE, with MIN_SIZE -
     139   * HEADER_SIZE bytes available to user.  Size argument to malloc is a signed
     140   * integer for sanity checking, so largest block size is 2^31.
     141   */
     142  #define NBUCKETS	29
     143  
     144  static struct free_list malloc_free_list[NBUCKETS];
     145  
     146  /* Initialization just sets everything to zero, but might be necessary on a
     147     machine where spin_lock_init does otherwise, and is necessary when
     148     running an executable that was written by something like Emacs's unexec.
     149     It preserves the values of data variables like malloc_free_list, but
     150     does not save the vm_allocate'd space allocated by this malloc.  */
     151  
     152  static void attribute_used_retain
     153  malloc_init (void)
     154  {
     155    int i;
     156    for (i = 0; i < NBUCKETS; ++i)
     157      {
     158        spin_lock_init (&malloc_free_list[i].lock);
     159        malloc_free_list[i].head = NULL;
     160  #ifdef DEBUG
     161        malloc_free_list[i].in_use = 0;
     162  #endif
     163      }
     164  }
     165  
     166  static void
     167  more_memory(int size, free_list_t fl)
     168  {
     169  	int amount;
     170  	int n;
     171  	vm_address_t where;
     172  	header_t h;
     173  	kern_return_t r;
     174  
     175  	if (size <= vm_page_size) {
     176  		amount = vm_page_size;
     177  		n = vm_page_size / size;
     178  		/* We lose vm_page_size - n*size bytes here.  */
     179  	} else {
     180  		amount = size;
     181  		n = 1;
     182  	}
     183  
     184  	r = vm_allocate(mach_task_self(), &where, (vm_size_t) amount, TRUE);
     185  	assert_perror (r);
     186  
     187  	h = (header_t) where;
     188  	do {
     189  		HEADER_NEXT (h) = fl->head;
     190  #ifdef MCHECK
     191  		HEADER_CHECK (h) = CHECK_FREE;
     192  #endif
     193  		fl->head = h;
     194  		h = (header_t) ((char *) h + size);
     195  	} while (--n != 0);
     196  }
     197  
     198  /* Declaration changed to standard one for GNU.  */
     199  void *
     200  malloc (size_t size)
     201  {
     202  	int i, n;
     203  	free_list_t fl;
     204  	header_t h;
     205  
     206  	if ((int) size < 0)		/* sanity check */
     207  		return 0;
     208  	size += HEADER_SIZE;
     209  	/*
     210  	 * Find smallest power-of-two block size
     211  	 * big enough to hold requested size plus header.
     212  	 */
     213  	i = 0;
     214  	n = MIN_SIZE;
     215  	while (n < size) {
     216  		i += 1;
     217  		n <<= 1;
     218  	}
     219  	assert(i < NBUCKETS);
     220  	fl = &malloc_free_list[i];
     221  	spin_lock(&fl->lock);
     222  	h = fl->head;
     223  	if (h == 0) {
     224  		/*
     225  		 * Free list is empty;
     226  		 * allocate more blocks.
     227  		 */
     228  		more_memory(n, fl);
     229  		h = fl->head;
     230  		if (h == 0) {
     231  			/*
     232  			 * Allocation failed.
     233  			 */
     234  			spin_unlock(&fl->lock);
     235  			return 0;
     236  		}
     237  	}
     238  	/*
     239  	 * Pop block from free list.
     240  	 */
     241  	fl->head = HEADER_NEXT (h);
     242  
     243  #ifdef MCHECK
     244  	assert (HEADER_CHECK (h) == CHECK_FREE);
     245  	HEADER_CHECK (h) = CHECK_BUSY;
     246  #endif
     247  
     248  #ifdef	DEBUG
     249  	fl->in_use += 1;
     250  #endif	/* DEBUG */
     251  	spin_unlock(&fl->lock);
     252  	/*
     253  	 * Store free list pointer in block header
     254  	 * so we can figure out where it goes
     255  	 * at free() time.
     256  	 */
     257  	HEADER_FREE (h) = fl;
     258  	/*
     259  	 * Return pointer past the block header.
     260  	 */
     261  	return ((char *) h) + HEADER_SIZE;
     262  }
     263  
     264  /* Declaration changed to standard one for GNU.  */
     265  void
     266  free (void *base)
     267  {
     268  	header_t h;
     269  	free_list_t fl;
     270  	int i;
     271  
     272  	if (base == 0)
     273  		return;
     274  	/*
     275  	 * Find free list for block.
     276  	 */
     277  	h = (header_t) (base - HEADER_SIZE);
     278  
     279  #ifdef MCHECK
     280  	assert (HEADER_CHECK (h) == CHECK_BUSY);
     281  #endif
     282  
     283  	fl = HEADER_FREE (h);
     284  	i = fl - malloc_free_list;
     285  	/*
     286  	 * Sanity checks.
     287  	 */
     288  	if (i < 0 || i >= NBUCKETS) {
     289  		assert(0 <= i && i < NBUCKETS);
     290  		return;
     291  	}
     292  	if (fl != &malloc_free_list[i]) {
     293  		assert(fl == &malloc_free_list[i]);
     294  		return;
     295  	}
     296  	/*
     297  	 * Push block on free list.
     298  	 */
     299  	spin_lock(&fl->lock);
     300  	HEADER_NEXT (h) = fl->head;
     301  #ifdef MCHECK
     302  	HEADER_CHECK (h) = CHECK_FREE;
     303  #endif
     304  	fl->head = h;
     305  #ifdef	DEBUG
     306  	fl->in_use -= 1;
     307  #endif	/* DEBUG */
     308  	spin_unlock(&fl->lock);
     309  	return;
     310  }
     311  
     312  /* Declaration changed to standard one for GNU.  */
     313  void *
     314  realloc (void *old_base, size_t new_size)
     315  {
     316  	header_t h;
     317  	free_list_t fl;
     318  	int i;
     319  	unsigned int old_size;
     320  	char *new_base;
     321  
     322  	if (old_base == 0)
     323  	  return malloc (new_size);
     324  
     325  	/*
     326  	 * Find size of old block.
     327  	 */
     328  	h = (header_t) (old_base - HEADER_SIZE);
     329  #ifdef MCHECK
     330  	assert (HEADER_CHECK (h) == CHECK_BUSY);
     331  #endif
     332  	fl = HEADER_FREE (h);
     333  	i = fl - malloc_free_list;
     334  	/*
     335  	 * Sanity checks.
     336  	 */
     337  	if (i < 0 || i >= NBUCKETS) {
     338  		assert(0 <= i && i < NBUCKETS);
     339  		return 0;
     340  	}
     341  	if (fl != &malloc_free_list[i]) {
     342  		assert(fl == &malloc_free_list[i]);
     343  		return 0;
     344  	}
     345  	/*
     346  	 * Free list with index i contains blocks of size
     347  	 * 2 ^ (i + * LOG2_MIN_SIZE) including header.
     348  	 */
     349  	old_size = (1 << (i + LOG2_MIN_SIZE)) - HEADER_SIZE;
     350  
     351  	if (new_size <= old_size
     352  	    && new_size > (((old_size + HEADER_SIZE) >> 1) - HEADER_SIZE))
     353  	  /* The new size still fits in the same block, and wouldn't fit in
     354  	     the next smaller block!  */
     355  	  return old_base;
     356  
     357  	/*
     358  	 * Allocate new block, copy old bytes, and free old block.
     359  	 */
     360  	new_base = malloc(new_size);
     361  	if (new_base)
     362  	  memcpy (new_base, old_base,
     363  		  (int) (old_size < new_size ? old_size : new_size));
     364  
     365  	if (new_base || new_size == 0)
     366  	  /* Free OLD_BASE, but only if the malloc didn't fail.  */
     367  	  free (old_base);
     368  
     369  	return new_base;
     370  }
     371  
     372  #ifdef	DEBUG
     373  void
     374  print_malloc_free_list (void)
     375  {
     376  	int i, size;
     377  	free_list_t fl;
     378  	int n;
     379  	header_t h;
     380  	int total_used = 0;
     381  	int total_free = 0;
     382  
     383  	fprintf(stderr, "      Size     In Use       Free      Total\n");
     384  	for (i = 0, size = MIN_SIZE, fl = malloc_free_list;
     385  	     i < NBUCKETS;
     386  	     i += 1, size <<= 1, fl += 1) {
     387  		spin_lock(&fl->lock);
     388  		if (fl->in_use != 0 || fl->head != 0) {
     389  			total_used += fl->in_use * size;
     390  			for (n = 0, h = fl->head; h != 0; h = HEADER_NEXT (h), n += 1)
     391  				;
     392  			total_free += n * size;
     393  			fprintf(stderr, "%10d %10d %10d %10d\n",
     394  				size, fl->in_use, n, fl->in_use + n);
     395  		}
     396  		spin_unlock(&fl->lock);
     397  	}
     398  	fprintf(stderr, " all sizes %10d %10d %10d\n",
     399  		total_used, total_free, total_used + total_free);
     400  }
     401  #endif	/* DEBUG */
     402  
     403  void
     404  _hurd_malloc_fork_prepare(void)
     405  /*
     406   * Prepare the malloc module for a fork by insuring that no thread is in a
     407   * malloc critical section.
     408   */
     409  {
     410      int i;
     411  
     412      for (i = 0; i < NBUCKETS; i++) {
     413  	spin_lock(&malloc_free_list[i].lock);
     414      }
     415  }
     416  
     417  void
     418  _hurd_malloc_fork_parent(void)
     419  /*
     420   * Called in the parent process after a fork() to resume normal operation.
     421   */
     422  {
     423      int i;
     424  
     425      for (i = NBUCKETS-1; i >= 0; i--) {
     426  	spin_unlock(&malloc_free_list[i].lock);
     427      }
     428  }
     429  
     430  void
     431  _hurd_malloc_fork_child(void)
     432  /*
     433   * Called in the child process after a fork() to resume normal operation.
     434   */
     435  {
     436      int i;
     437  
     438      for (i = NBUCKETS-1; i >= 0; i--) {
     439  	spin_unlock(&malloc_free_list[i].lock);
     440      }
     441  }
     442  
     443  
     444  SET_RELHOOK (_hurd_preinit_hook, malloc_init);