~ubuntu-branches/ubuntu/quantal/libgc/quantal

« back to all changes in this revision

Viewing changes to libatomic_ops-1.2/src/atomic_ops_malloc.c

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Egger
  • Date: 2011-02-19 12:19:56 UTC
  • mfrom: (1.3.2 upstream) (0.1.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 14.
  • Revision ID: james.westby@ubuntu.com-20110219121956-67rb69xlt5nud3v2
Tags: 1:7.1-5
Upload to unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  
 
2
 * Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
 
3
 * Original Author: Hans Boehm
 
4
 *
 
5
 * This file may be redistributed and/or modified under the
 
6
 * terms of the GNU General Public License as published by the Free Software
 
7
 * Foundation; either version 2, or (at your option) any later version.
 
8
 * 
 
9
 * It is distributed in the hope that it will be useful, but WITHOUT ANY
 
10
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
11
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License in the
 
12
 * file doc/COPYING for more details.
 
13
 */
 
14
 
 
15
#if defined(HAVE_CONFIG_H)
 
16
# include "config.h"
 
17
#endif
 
18
 
 
19
#define AO_REQUIRE_CAS
 
20
#include "atomic_ops_stack.h"
 
21
#include <string.h>     /* for ffs, which is assumed reentrant. */
 
22
#include <stdlib.h>
 
23
#ifdef AO_TRACE_MALLOC
 
24
# include <stdio.h>
 
25
# include <pthread.h>
 
26
#endif
 
27
 
 
28
/*
 
29
 * We round up each allocation request to the next power of two
 
30
 * minus one word.
 
31
 * We keep one stack of free objects for each size.  Each object
 
32
 * has an initial word (offset -sizeof(AO_t) from the visible pointer)
 
33
 * which contains either
 
34
 *      The binary log of the object size in bytes (small objects)
 
35
 *      The object size (a multiple of CHUNK_SIZE) for large objects.
 
36
 * The second case only arises if mmap-based allocation is supported.
 
37
 * We align the user-visible part of each object on a GRANULARITY
 
38
 * byte boundary.  That means that the actual (hidden) start of
 
39
 * the object starts a word before this boundary.
 
40
 */
 
41
 
 
42
#ifndef LOG_MAX_SIZE
 
43
# define LOG_MAX_SIZE 16
 
44
        /* We assume that 2**LOG_MAX_SIZE is a multiple of page size. */
 
45
#endif
 
46
 
 
47
#ifndef ALIGNMENT
 
48
# define ALIGNMENT 16
 
49
        /* Assumed to be at least sizeof(AO_t).         */
 
50
#endif
 
51
 
 
52
#define CHUNK_SIZE (1 << LOG_MAX_SIZE)
 
53
 
 
54
#ifndef AO_INITIAL_HEAP_SIZE
 
55
#  define AO_INITIAL_HEAP_SIZE (2*(LOG_MAX_SIZE+1)*CHUNK_SIZE)
 
56
#endif
 
57
 
 
58
char AO_initial_heap[AO_INITIAL_HEAP_SIZE];
 
59
 
 
60
static volatile AO_t initial_heap_ptr = (AO_t)AO_initial_heap;
 
61
static volatile char *initial_heap_lim = AO_initial_heap + AO_INITIAL_HEAP_SIZE;
 
62
 
 
63
#if defined(HAVE_MMAP)
 
64
 
 
65
#include <sys/types.h>
 
66
#include <sys/stat.h>
 
67
#include <fcntl.h>
 
68
#include <sys/mman.h>
 
69
 
 
70
static volatile AO_t mmap_enabled = 0;
 
71
 
 
72
void
 
73
AO_malloc_enable_mmap(void)
 
74
{
 
75
  AO_store(&mmap_enabled, 1);
 
76
}
 
77
 
 
78
static char *get_mmaped(size_t sz)
 
79
{
 
80
  char * result;
 
81
 
 
82
  assert(!(sz & (CHUNK_SIZE - 1)));
 
83
  if (!mmap_enabled) return 0;
 
84
# if defined(MAP_ANONYMOUS)
 
85
    result = mmap(0, sz, PROT_READ | PROT_WRITE,
 
86
                  MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
 
87
# elif defined(MAP_ANON)
 
88
    result = mmap(0, sz, PROT_READ | PROT_WRITE,
 
89
                  MAP_PRIVATE | MAP_ANON, -1, 0);
 
90
# else
 
91
    {
 
92
      int zero_fd = open("/dev/zero", O_RDONLY);
 
93
      result = mmap(0, sz, PROT_READ | PROT_WRITE,
 
94
                    MAP_PRIVATE, zero_fd, 0);
 
95
      close(zero_fd);
 
96
    }
 
97
# endif
 
98
  if (result == MAP_FAILED) result = 0;
 
99
  return result;
 
100
}
 
101
 
 
102
/* Allocate an object of size (incl. header) of size > CHUNK_SIZE.      */
 
103
/* sz includes space for an AO_t-sized header.                          */
 
104
static char *
 
105
AO_malloc_large(size_t sz)
 
106
{
 
107
 char * result;
 
108
 /* The header will force us to waste ALIGNMENT bytes, incl. header.    */
 
109
   sz += ALIGNMENT;
 
110
 /* Round to multiple of CHUNK_SIZE.    */
 
111
   sz = (sz + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
 
112
 result = get_mmaped(sz);
 
113
 if (result == 0) return 0;
 
114
 result += ALIGNMENT;
 
115
 ((AO_t *)result)[-1] = (AO_t)sz;
 
116
 return result;
 
117
}
 
118
 
 
119
static void
 
120
AO_free_large(char * p)
 
121
{
 
122
  AO_t sz = ((AO_t *)p)[-1];
 
123
  if (munmap(p - ALIGNMENT, (size_t)sz) != 0)
 
124
    abort();  /* Programmer error.  Not really async-signal-safe, but ... */
 
125
}
 
126
  
 
127
 
 
128
#else /*  No MMAP */
 
129
 
 
130
void
 
131
AO_malloc_enable_mmap(void)
 
132
{
 
133
}
 
134
 
 
135
static char *get_mmaped(size_t sz)
 
136
{
 
137
  return 0;
 
138
}
 
139
 
 
140
static char *
 
141
AO_malloc_large(size_t sz)
 
142
{
 
143
  return 0;
 
144
}
 
145
 
 
146
static void
 
147
AO_free_large(char * p)
 
148
{
 
149
  abort();  /* Programmer error.  Not really async-signal-safe, but ... */
 
150
}
 
151
 
 
152
#endif /* No MMAP */
 
153
 
 
154
static char *
 
155
get_chunk(void)
 
156
{
 
157
  char *initial_ptr;
 
158
  char *my_chunk_ptr;
 
159
  char * my_lim;
 
160
 
 
161
retry:
 
162
  initial_ptr = (char *)AO_load(&initial_heap_ptr);
 
163
  my_chunk_ptr = (char *)(((AO_t)initial_ptr + (ALIGNMENT - 1))
 
164
                          & ~(ALIGNMENT - 1));
 
165
  if (initial_ptr != my_chunk_ptr)
 
166
    {
 
167
      /* Align correctly.  If this fails, someone else did it for us.   */
 
168
      AO_compare_and_swap_acquire(&initial_heap_ptr, (AO_t)initial_ptr,
 
169
                                  (AO_t)my_chunk_ptr);
 
170
    }
 
171
  my_lim = my_chunk_ptr + CHUNK_SIZE;
 
172
  if (my_lim <= initial_heap_lim)
 
173
    {
 
174
      if (!AO_compare_and_swap(&initial_heap_ptr, (AO_t)my_chunk_ptr,
 
175
                                                  (AO_t)my_lim))
 
176
        goto retry;
 
177
      return my_chunk_ptr;
 
178
    }
 
179
  /* We failed.  The initial heap is used up.   */
 
180
  my_chunk_ptr = get_mmaped(CHUNK_SIZE);
 
181
  assert (!((AO_t)my_chunk_ptr & (ALIGNMENT-1)));
 
182
  return my_chunk_ptr;
 
183
}
 
184
 
 
185
/* Object free lists.  Ith entry corresponds to objects */
 
186
/* of total size 2**i bytes.                                    */
 
187
AO_stack_t AO_free_list[LOG_MAX_SIZE+1];
 
188
 
 
189
/* Chunk free list, linked through first word in chunks.        */
 
190
/* All entries of size CHUNK_SIZE.                              */
 
191
AO_stack_t AO_chunk_free_list;
 
192
 
 
193
/* Break up the chunk, and add it to the object free list for   */
 
194
/* the given size.  Sz must be a power of two.                  */
 
195
/* We have exclusive access to chunk.                           */
 
196
static void
 
197
add_chunk_as(void * chunk, size_t sz, unsigned log_sz)
 
198
{
 
199
  char *first = (char *)chunk + ALIGNMENT - sizeof(AO_t);
 
200
  char *limit = (char *)chunk + CHUNK_SIZE - sz;
 
201
  char *next, *p;
 
202
 
 
203
  for (p = first; p <= limit; p = next) {
 
204
    next = p + sz;
 
205
    AO_stack_push(AO_free_list+log_sz, (AO_t *)p);
 
206
  }
 
207
}
 
208
 
 
209
static int msbs[16] = {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4};
 
210
 
 
211
/* Return the position of the most significant set bit in the   */
 
212
/* argument.                                                    */
 
213
/* We follow the conventions of ffs(), i.e. the least           */
 
214
/* significant bit is number one.                               */
 
215
int msb(size_t s)
 
216
{
 
217
  int result = 0;
 
218
  if ((s & 0xff) != s) {
 
219
    /* The following shift often generates warnings on 32-bit arch's    */
 
220
    /* That's OK, because it will never be executed there.              */
 
221
    if (sizeof(size_t) > 4 && (s >> 32) != 0)
 
222
      {
 
223
        s >>= 32;
 
224
        result += 32;
 
225
      }
 
226
    if ((s >> 16) != 0)
 
227
      {
 
228
        s >>= 16;
 
229
        result += 16;
 
230
      }
 
231
    if ((s >> 8) != 0)
 
232
      {
 
233
        s >>= 8;
 
234
        result += 8;
 
235
      }
 
236
  }
 
237
  if (s > 15)
 
238
    {
 
239
      s >>= 4;
 
240
      result += 4;
 
241
    }
 
242
  result += msbs[s];
 
243
  return result;
 
244
}
 
245
 
 
246
void *
 
247
AO_malloc(size_t sz)
 
248
{
 
249
  AO_t *result;
 
250
  size_t adj_sz = sz + sizeof(AO_t);
 
251
  int log_sz;
 
252
  if (sz > CHUNK_SIZE)
 
253
    return AO_malloc_large(sz);
 
254
  log_sz = msb(adj_sz-1);
 
255
  result = AO_stack_pop(AO_free_list+log_sz);
 
256
  while (0 == result) {
 
257
    void * chunk = get_chunk();
 
258
    if (0 == chunk) return 0;
 
259
    adj_sz = 1 << log_sz;
 
260
    add_chunk_as(chunk, adj_sz, log_sz);
 
261
    result = AO_stack_pop(AO_free_list+log_sz);
 
262
  }
 
263
  *result = log_sz;
 
264
# ifdef AO_TRACE_MALLOC
 
265
    fprintf(stderr, "%x: AO_malloc(%lu) = %p\n",
 
266
                    (int)pthread_self(), (unsigned long)sz, result+1);
 
267
# endif
 
268
  return result + 1;
 
269
}
 
270
 
 
271
void
 
272
AO_free(void *p)
 
273
{
 
274
  char *base = (char *)p - sizeof(AO_t);
 
275
  int log_sz;
 
276
 
 
277
  if (0 == p) return;
 
278
  log_sz = *(AO_t *)base;
 
279
# ifdef AO_TRACE_MALLOC
 
280
    fprintf(stderr, "%x: AO_free(%p sz:%lu)\n", (int)pthread_self(), p,
 
281
                    (unsigned long)
 
282
                      (log_sz > LOG_MAX_SIZE? log_sz : (1 << log_sz)));
 
283
# endif
 
284
  if (log_sz > LOG_MAX_SIZE)
 
285
    AO_free_large(p);
 
286
  else
 
287
    AO_stack_push(AO_free_list+log_sz, (AO_t *)base);
 
288
}