123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- #include <stdlib.h>
- #include <string.h>
- #include "hurdmalloc.h" /* XXX see that file */
- #include <mach.h>
- #define vm_allocate __vm_allocate
- #define vm_page_size __vm_page_size
- /*
- * Mach Operating System
- * Copyright (c) 1991,1990,1989 Carnegie Mellon University
- * All Rights Reserved.
- *
- * Permission to use, copy, modify and distribute this software and its
- * documentation is hereby granted, provided that both the copyright
- * notice and this permission notice appear in all copies of the
- * software, derivative works or modified versions, and any portions
- * thereof, and that both notices appear in supporting documentation.
- *
- * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
- * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
- * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- *
- * Carnegie Mellon requests users of this software to return to
- *
- * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
- * School of Computer Science
- * Carnegie Mellon University
- * Pittsburgh PA 15213-3890
- *
- * any improvements or extensions that they make and grant Carnegie Mellon
- * the rights to redistribute these changes.
- */
- /*
- * (pre-GNU) HISTORY
- *
- * Revision 2.7 91/05/14 17:57:34 mrt
- * Correcting copyright
- *
- * Revision 2.6 91/02/14 14:20:26 mrt
- * Added new Mach copyright
- * [91/02/13 12:41:21 mrt]
- *
- * Revision 2.5 90/11/05 14:37:33 rpd
- * Added malloc_fork* code.
- * [90/11/02 rwd]
- *
- * Add spin_lock_t.
- * [90/10/31 rwd]
- *
- * Revision 2.4 90/08/07 14:31:28 rpd
- * Removed RCS keyword nonsense.
- *
- * Revision 2.3 90/06/02 15:14:00 rpd
- * Converted to new IPC.
- * [90/03/20 20:56:57 rpd]
- *
- * Revision 2.2 89/12/08 19:53:59 rwd
- * Removed conditionals.
- * [89/10/23 rwd]
- *
- * Revision 2.1 89/08/03 17:09:46 rwd
- * Created.
- *
- *
- * 13-Sep-88 Eric Cooper (ecc) at Carnegie Mellon University
- * Changed realloc() to copy min(old size, new size) bytes.
- * Bug found by Mike Kupfer at Olivetti.
- */
- /*
- * File: malloc.c
- * Author: Eric Cooper, Carnegie Mellon University
- * Date: July, 1988
- *
- * Memory allocator for use with multiple threads.
- */
- #include <assert.h>
- #include <cthreads.h>
- #define MCHECK
- /*
- * Structure of memory block header.
- * When free, next points to next block on free list.
- * When allocated, fl points to free list.
- * Size of header is 4 bytes, so minimum usable block size is 8 bytes.
- */
- #define CHECK_BUSY 0x8a3c743e
- #define CHECK_FREE 0x66688b92
- #ifdef MCHECK
- typedef struct header {
- long check;
- union {
- struct header *next;
- struct free_list *fl;
- } u;
- } *header_t;
- #define HEADER_SIZE sizeof (struct header)
- #define HEADER_NEXT(h) ((h)->u.next)
- #define HEADER_FREE(h) ((h)->u.fl)
- #define HEADER_CHECK(h) ((h)->check)
- #define MIN_SIZE 16
- #define LOG2_MIN_SIZE 4
- #else /* ! MCHECK */
- typedef union header {
- union header *next;
- struct free_list *fl;
- } *header_t;
- #define HEADER_SIZE sizeof (union header)
- #define HEADER_NEXT(h) ((h)->next)
- #define HEADER_FREE(h) ((h)->fl)
- #define MIN_SIZE 8 /* minimum block size */
- #define LOG2_MIN_SIZE 3
- #endif /* MCHECK */
- typedef struct free_list {
- spin_lock_t lock; /* spin lock for mutual exclusion */
- header_t head; /* head of free list for this size */
- #ifdef DEBUG
- int in_use; /* # mallocs - # frees */
- #endif /* DEBUG */
- } *free_list_t;
- /*
- * Free list with index i contains blocks of size 2 ^ (i + LOG2_MIN_SIZE)
- * including header. Smallest block size is MIN_SIZE, with MIN_SIZE -
- * HEADER_SIZE bytes available to user. Size argument to malloc is a signed
- * integer for sanity checking, so largest block size is 2^31.
- */
- #define NBUCKETS 29
- static struct free_list malloc_free_list[NBUCKETS];
- /* Initialization just sets everything to zero, but might be necessary on a
- machine where spin_lock_init does otherwise, and is necessary when
- running an executable that was written by something like Emacs's unexec.
- It preserves the values of data variables like malloc_free_list, but
- does not save the vm_allocate'd space allocated by this malloc. */
- static void
- malloc_init (void)
- {
- int i;
- for (i = 0; i < NBUCKETS; ++i)
- {
- spin_lock_init (&malloc_free_list[i].lock);
- malloc_free_list[i].head = NULL;
- #ifdef DEBUG
- malloc_free_list[i].in_use = 0;
- #endif
- }
- /* This not only suppresses a `defined but not used' warning,
- but it is ABSOLUTELY NECESSARY to avoid the hyperclever
- compiler from "optimizing out" the entire function! */
- (void) &malloc_init;
- }
- static void
- more_memory(int size, free_list_t fl)
- {
- int amount;
- int n;
- vm_address_t where;
- header_t h;
- kern_return_t r;
- if (size <= vm_page_size) {
- amount = vm_page_size;
- n = vm_page_size / size;
- /* We lose vm_page_size - n*size bytes here. */
- } else {
- amount = size;
- n = 1;
- }
- r = vm_allocate(mach_task_self(), &where, (vm_size_t) amount, TRUE);
- assert_perror (r);
- h = (header_t) where;
- do {
- HEADER_NEXT (h) = fl->head;
- #ifdef MCHECK
- HEADER_CHECK (h) = CHECK_FREE;
- #endif
- fl->head = h;
- h = (header_t) ((char *) h + size);
- } while (--n != 0);
- }
- /* Declaration changed to standard one for GNU. */
- void *
- malloc (size_t size)
- {
- int i, n;
- free_list_t fl;
- header_t h;
- if ((int) size < 0) /* sanity check */
- return 0;
- size += HEADER_SIZE;
- /*
- * Find smallest power-of-two block size
- * big enough to hold requested size plus header.
- */
- i = 0;
- n = MIN_SIZE;
- while (n < size) {
- i += 1;
- n <<= 1;
- }
- ASSERT(i < NBUCKETS);
- fl = &malloc_free_list[i];
- spin_lock(&fl->lock);
- h = fl->head;
- if (h == 0) {
- /*
- * Free list is empty;
- * allocate more blocks.
- */
- more_memory(n, fl);
- h = fl->head;
- if (h == 0) {
- /*
- * Allocation failed.
- */
- spin_unlock(&fl->lock);
- return 0;
- }
- }
- /*
- * Pop block from free list.
- */
- fl->head = HEADER_NEXT (h);
- #ifdef MCHECK
- assert (HEADER_CHECK (h) == CHECK_FREE);
- HEADER_CHECK (h) = CHECK_BUSY;
- #endif
- #ifdef DEBUG
- fl->in_use += 1;
- #endif /* DEBUG */
- spin_unlock(&fl->lock);
- /*
- * Store free list pointer in block header
- * so we can figure out where it goes
- * at free() time.
- */
- HEADER_FREE (h) = fl;
- /*
- * Return pointer past the block header.
- */
- return ((char *) h) + HEADER_SIZE;
- }
- /* Declaration changed to standard one for GNU. */
- void
- free (void *base)
- {
- header_t h;
- free_list_t fl;
- int i;
- if (base == 0)
- return;
- /*
- * Find free list for block.
- */
- h = (header_t) (base - HEADER_SIZE);
- #ifdef MCHECK
- assert (HEADER_CHECK (h) == CHECK_BUSY);
- #endif
- fl = HEADER_FREE (h);
- i = fl - malloc_free_list;
- /*
- * Sanity checks.
- */
- if (i < 0 || i >= NBUCKETS) {
- ASSERT(0 <= i && i < NBUCKETS);
- return;
- }
- if (fl != &malloc_free_list[i]) {
- ASSERT(fl == &malloc_free_list[i]);
- return;
- }
- /*
- * Push block on free list.
- */
- spin_lock(&fl->lock);
- HEADER_NEXT (h) = fl->head;
- #ifdef MCHECK
- HEADER_CHECK (h) = CHECK_FREE;
- #endif
- fl->head = h;
- #ifdef DEBUG
- fl->in_use -= 1;
- #endif /* DEBUG */
- spin_unlock(&fl->lock);
- return;
- }
- /* Declaration changed to standard one for GNU. */
- void *
- realloc (void *old_base, size_t new_size)
- {
- header_t h;
- free_list_t fl;
- int i;
- unsigned int old_size;
- char *new_base;
- if (old_base == 0)
- return malloc (new_size);
- /*
- * Find size of old block.
- */
- h = (header_t) (old_base - HEADER_SIZE);
- #ifdef MCHECK
- assert (HEADER_CHECK (h) == CHECK_BUSY);
- #endif
- fl = HEADER_FREE (h);
- i = fl - malloc_free_list;
- /*
- * Sanity checks.
- */
- if (i < 0 || i >= NBUCKETS) {
- ASSERT(0 <= i && i < NBUCKETS);
- return 0;
- }
- if (fl != &malloc_free_list[i]) {
- ASSERT(fl == &malloc_free_list[i]);
- return 0;
- }
- /*
- * Free list with index i contains blocks of size
- * 2 ^ (i + * LOG2_MIN_SIZE) including header.
- */
- old_size = (1 << (i + LOG2_MIN_SIZE)) - HEADER_SIZE;
- if (new_size <= old_size
- && new_size > (((old_size + HEADER_SIZE) >> 1) - HEADER_SIZE))
- /* The new size still fits in the same block, and wouldn't fit in
- the next smaller block! */
- return old_base;
- /*
- * Allocate new block, copy old bytes, and free old block.
- */
- new_base = malloc(new_size);
- if (new_base)
- memcpy (new_base, old_base,
- (int) (old_size < new_size ? old_size : new_size));
- if (new_base || new_size == 0)
- /* Free OLD_BASE, but only if the malloc didn't fail. */
- free (old_base);
- return new_base;
- }
- #ifdef DEBUG
- void
- print_malloc_free_list (void)
- {
- int i, size;
- free_list_t fl;
- int n;
- header_t h;
- int total_used = 0;
- int total_free = 0;
- fprintf(stderr, " Size In Use Free Total\n");
- for (i = 0, size = MIN_SIZE, fl = malloc_free_list;
- i < NBUCKETS;
- i += 1, size <<= 1, fl += 1) {
- spin_lock(&fl->lock);
- if (fl->in_use != 0 || fl->head != 0) {
- total_used += fl->in_use * size;
- for (n = 0, h = fl->head; h != 0; h = HEADER_NEXT (h), n += 1)
- ;
- total_free += n * size;
- fprintf(stderr, "%10d %10d %10d %10d\n",
- size, fl->in_use, n, fl->in_use + n);
- }
- spin_unlock(&fl->lock);
- }
- fprintf(stderr, " all sizes %10d %10d %10d\n",
- total_used, total_free, total_used + total_free);
- }
- #endif /* DEBUG */
- void
- _hurd_malloc_fork_prepare(void)
- /*
- * Prepare the malloc module for a fork by insuring that no thread is in a
- * malloc critical section.
- */
- {
- int i;
- for (i = 0; i < NBUCKETS; i++) {
- spin_lock(&malloc_free_list[i].lock);
- }
- }
- void
- _hurd_malloc_fork_parent(void)
- /*
- * Called in the parent process after a fork() to resume normal operation.
- */
- {
- int i;
- for (i = NBUCKETS-1; i >= 0; i--) {
- spin_unlock(&malloc_free_list[i].lock);
- }
- }
- void
- _hurd_malloc_fork_child(void)
- /*
- * Called in the child process after a fork() to resume normal operation.
- */
- {
- int i;
- for (i = NBUCKETS-1; i >= 0; i--) {
- spin_unlock(&malloc_free_list[i].lock);
- }
- }
- text_set_element (_hurd_preinit_hook, malloc_init);
|