summaryrefslogtreecommitdiff
path: root/libc/stdlib/malloc/malloc.c
blob: 54e68a5f301d67df3d018359f8a4942d121f96f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * libc/stdlib/malloc/malloc.c -- malloc function
 *
 *  Copyright (C) 2002  NEC Corporation
 *  Copyright (C) 2002  Miles Bader <miles@gnu.org>
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License.  See the file COPYING.LIB in the main
 * directory of this archive for more details.
 * 
 * Written by Miles Bader <miles@gnu.org>
 */

#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

#include "malloc.h"
#include "heap.h"


/* The malloc heap.  */
struct heap __malloc_heap = HEAP_INIT;

#ifdef MALLOC_USE_LOCKING
/* A lock protecting the malloc heap.  */
malloc_mutex_t __malloc_lock;
# ifdef MALLOC_USE_SBRK
/* A lock protecting our use of sbrk.  */
malloc_mutex_t __malloc_sbrk_lock;
# endif /* MALLOC_USE_SBRK */
#endif /* MALLOC_USE_LOCKING */


void *
malloc (size_t size)
{
  void *mem;
  struct heap *heap = &__malloc_heap;

  MALLOC_DEBUG ("malloc: %d bytes\n", size);

  /* Include extra space to record the size of the allocated block.  */
  size += MALLOC_HEADER_SIZE;

  __malloc_lock ();

  mem = __heap_alloc (heap, &size);
  if (! mem) 
    /* We couldn't allocate from the heap, so get some more memory
       from the system, add it to the heap, and try again.  */
    {
      /* If we're trying to allocate a block bigger than the default
	 MALLOC_HEAP_EXTEND_SIZE, make sure we get enough to hold it. */
      void *block;
      size_t block_size
	= (size < MALLOC_HEAP_EXTEND_SIZE
	   ? MALLOC_HEAP_EXTEND_SIZE
	   : MALLOC_ROUND_UP_TO_PAGE_SIZE (size));

#ifdef MALLOC_USE_SBRK
      /* Get the sbrk lock while we've still got the main lock.  */
      __malloc_lock_sbrk ();
#endif

      /* Don't hold the main lock during the syscall, so that small
	 allocations in a different thread may succeed while we're
	 blocked.  */
      __malloc_unlock ();

      /* Allocate the new heap block.  */
#ifdef MALLOC_USE_SBRK

      /* Use sbrk we can, as it's faster than mmap, and guarantees
	 contiguous allocation.  */
      block = sbrk (block_size);
      if (block != (void *)-1)
	{
	  /* Because sbrk can return results of arbitrary
	     alignment, align the result to a MALLOC_ALIGNMENT boundary.  */
	  long aligned_block = MALLOC_ROUND_UP ((long)block, MALLOC_ALIGNMENT);
	  if (block != (void *)aligned_block)
	    /* Have to adjust.  We should only have to actually do this
	       the first time (after which we will have aligned the brk
	       correctly).  */
	    {
	      /* Move the brk to reflect the alignment; our next allocation
		 should start on exactly the right alignment.  */
	      sbrk (aligned_block - (long)block);
	      block = (void *)aligned_block;
	    }
	}
      __malloc_unlock_sbrk ();

#else /* !MALLOC_USE_SBRK */

      /* Otherwise, use mmap.  */
      block = mmap (0, block_size, PROT_READ | PROT_WRITE,
		    MAP_SHARED | MAP_ANONYMOUS, 0, 0);

#endif /* MALLOC_USE_SBRK */

      /* Get back the main lock.  */
      __malloc_lock ();

      if (block != (void *)-1)
	{
	  MALLOC_DEBUG ("  adding memory: 0x%lx - 0x%lx (%d bytes)\n",
			(long)block, (long)block + block_size, block_size);

	  /* Put BLOCK into the heap.  */
	  __heap_free (heap, block, block_size);

	  /* Try again to allocate.  */
	  mem = __heap_alloc (heap, &size);
	}
    }

  __malloc_unlock ();

  if (mem)
    /* Record the size of this block.  */
    {
      mem = MALLOC_ADDR (mem);
      MALLOC_SET_SIZE (mem, size);

      MALLOC_DEBUG ("  malloc: returning 0x%lx (base:0x%lx, total_size:%d)\n",
		    (long)mem, (long)mem - MALLOC_ALIGNMENT, size);
    }

  return mem;
}