resource.c 8.46 KB
Newer Older
1 2 3
/*
 *	BIRD Resource Manager
 *
4
 *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
5 6 7 8 9 10
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include <stdio.h>
#include <stdlib.h>
11
#include <stdint.h>
12 13 14

#include "nest/bird.h"
#include "lib/resource.h"
15
#include "lib/string.h"
16

17 18 19 20 21 22 23 24 25 26 27 28 29 30
/**
 * DOC: Resource pools
 *
 * Resource pools (&pool) are just containers holding a list of
 * other resources. Freeing a pool causes all the listed resources
 * to be freed as well. Each existing &resource is linked to some pool
 * except for a root pool which isn't linked anywhere, so all the
 * resources form a tree structure with internal nodes corresponding
 * to pools and leaves being the other resources.
 *
 * Example: Almost all modules of BIRD have their private pool which
 * is freed upon shutdown of the module.
 */

31 32 33
struct pool {
  resource r;
  list inside;
34
  const char *name;
35 36
};

37 38
static void pool_dump(resource *);
static void pool_free(resource *);
39
static resource *pool_lookup(resource *, unsigned long);
40
static size_t pool_memsize(resource *P);
41 42 43 44 45

static struct resclass pool_class = {
  "Pool",
  sizeof(pool),
  pool_free,
46
  pool_dump,
47 48
  pool_lookup,
  pool_memsize
49 50 51 52 53 54
};

pool root_pool;

static int indent;

55 56 57 58 59 60 61 62
/**
 * rp_new - create a resource pool
 * @p: parent pool
 * @name: pool name (to be included in debugging dumps)
 *
 * rp_new() creates a new resource pool inside the specified
 * parent pool.
 */
63
pool *
64
rp_new(pool *p, const char *name)
65 66
{
  pool *z = ralloc(p, &pool_class);
67
  z->name = name;
68 69 70 71
  init_list(&z->inside);
  return z;
}

72
static void
73 74 75 76 77 78 79 80 81 82 83 84 85 86
pool_free(resource *P)
{
  pool *p = (pool *) P;
  resource *r, *rr;

  r = HEAD(p->inside);
  while (rr = (resource *) r->n.next)
    {
      r->class->free(r);
      xfree(r);
      r = rr;
    }
}

87
static void
88 89 90 91 92
pool_dump(resource *P)
{
  pool *p = (pool *) P;
  resource *r;

93
  debug("%s\n", p->name);
94 95 96 97 98 99
  indent += 3;
  WALK_LIST(r, p->inside)
    rdump(r);
  indent -= 3;
}

100 101 102 103 104 105 106 107 108 109 110 111 112
static size_t
pool_memsize(resource *P)
{
  pool *p = (pool *) P;
  resource *r;
  size_t sum = sizeof(pool) + ALLOC_OVERHEAD;

  WALK_LIST(r, p->inside)
    sum += rmemsize(r);

  return sum;
}

113 114 115 116 117 118 119 120 121 122 123 124
static resource *
pool_lookup(resource *P, unsigned long a)
{
  pool *p = (pool *) P;
  resource *r, *q;

  WALK_LIST(r, p->inside)
    if (r->class->lookup && (q = r->class->lookup(r, a)))
      return q;
  return NULL;
}

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
/**
 * rmove - move a resource
 * @res: resource
 * @p: pool to move the resource to
 *
 * rmove() moves a resource from one pool to another.
 */

void rmove(void *res, pool *p)
{
  resource *r = res;

  if (r)
    {
      if (r->n.next)
        rem_node(&r->n);
      add_tail(&p->inside, &r->n);
    }
}

145 146 147 148 149 150 151 152 153 154
/**
 * rfree - free a resource
 * @res: resource
 *
 * rfree() frees the given resource and all information associated
 * with it. In case it's a resource pool, it also frees all the objects
 * living inside the pool.
 *
 * It works by calling a class-specific freeing function.
 */
155 156 157 158 159
void
rfree(void *res)
{
  resource *r = res;

160 161 162 163 164 165
  if (!r)
    return;

  if (r->n.next)
    rem_node(&r->n);
  r->class->free(r);
166
  r->class = NULL;
167
  xfree(r);
168 169
}

170 171 172 173 174 175 176 177 178
/**
 * rdump - dump a resource
 * @res: resource
 *
 * This function prints out all available information about the given
 * resource to the debugging output.
 *
 * It works by calling a class-specific dump function.
 */
179 180 181 182 183 184
void
rdump(void *res)
{
  char x[16];
  resource *r = res;

185 186
  bsprintf(x, "%%%ds%%p ", indent);
  debug(x, "", r);
187 188
  if (r)
    {
189
      debug("%s ", r->class->name);
190 191 192 193 194 195
      r->class->dump(r);
    }
  else
    debug("NULL\n");
}

196 197 198 199 200 201 202 203 204 205 206
size_t
rmemsize(void *res)
{
  resource *r = res;
  if (!r)
    return 0;
  if (!r->class->memsize)
    return r->class->size + ALLOC_OVERHEAD;
  return r->class->memsize(r);
}

207 208 209 210 211 212 213
/**
 * ralloc - create a resource
 * @p: pool to create the resource in
 * @c: class of the new resource
 *
 * This function is called by the resource classes to create a new
 * resource of the specified class and link it to the given pool.
214 215
 * Allocated memory is zeroed. Size of the resource structure is taken
 * from the @size field of the &resclass.
216
 */
217 218 219 220
void *
ralloc(pool *p, struct resclass *c)
{
  resource *r = xmalloc(c->size);
221
  bzero(r, c->size);
222 223

  r->class = c;
224 225
  if (p)
    add_tail(&p->inside, &r->n);
226 227 228
  return r;
}

229 230 231 232 233 234 235 236 237 238 239
/**
 * rlookup - look up a memory location
 * @a: memory address
 *
 * This function examines all existing resources to see whether
 * the address @a is inside any resource. It's used for debugging
 * purposes only.
 *
 * It works by calling a class-specific lookup function for each
 * resource.
 */
240 241 242 243 244 245 246 247 248 249 250 251
void
rlookup(unsigned long a)
{
  resource *r;

  debug("Looking up %08lx\n", a);
  if (r = pool_lookup(&root_pool.r, a))
    rdump(r);
  else
    debug("Not found.\n");
}

252 253 254 255 256 257 258
/**
 * resource_init - initialize the resource manager
 *
 * This function is called during BIRD startup. It initializes
 * all data structures of the resource manager and creates the
 * root pool.
 */
259 260 261 262
void
resource_init(void)
{
  root_pool.r.class = &pool_class;
263
  root_pool.name = "Root";
264 265 266
  init_list(&root_pool.inside);
}

267 268 269 270 271 272 273 274 275 276 277
/**
 * DOC: Memory blocks
 *
 * Memory blocks are pieces of contiguous allocated memory.
 * They are a bit non-standard since they are represented not by a pointer
 * to &resource, but by a void pointer to the start of data of the
 * memory block. All memory block functions know how to locate the header
 * given the data pointer.
 *
 * Example: All "unique" data structures such as hash tables are allocated
 * as memory blocks.
278 279 280 281 282
 */

struct mblock {
  resource r;
  unsigned size;
283
  uintptr_t data_align[0];
284 285 286
  byte data[0];
};

Martin Mareš's avatar
Martin Mareš committed
287
static void mbl_free(resource *r UNUSED)
288 289 290
{
}

291
static void mbl_debug(resource *r)
292 293 294 295 296 297
{
  struct mblock *m = (struct mblock *) r;

  debug("(size=%d)\n", m->size);
}

298 299 300 301 302 303 304 305 306 307
static resource *
mbl_lookup(resource *r, unsigned long a)
{
  struct mblock *m = (struct mblock *) r;

  if ((unsigned long) m->data <= a && (unsigned long) m->data + m->size > a)
    return r;
  return NULL;
}

308 309 310 311 312 313 314
static size_t
mbl_memsize(resource *r)
{
  struct mblock *m = (struct mblock *) r;
  return ALLOC_OVERHEAD + sizeof(struct mblock) + m->size;
}

315
static struct resclass mb_class = {
316 317 318 319
  "Memory",
  0,
  mbl_free,
  mbl_debug,
320 321
  mbl_lookup,
  mbl_memsize
322 323
};

324 325 326 327 328 329 330 331 332 333 334 335 336
/**
 * mb_alloc - allocate a memory block
 * @p: pool
 * @size: size of the block
 *
 * mb_alloc() allocates memory of a given size and creates
 * a memory block resource representing this memory chunk
 * in the pool @p.
 *
 * Please note that mb_alloc() returns a pointer to the memory
 * chunk, not to the resource, hence you have to free it using
 * mb_free(), not rfree().
 */
337 338 339 340 341 342 343 344 345 346 347
void *
mb_alloc(pool *p, unsigned size)
{
  struct mblock *b = xmalloc(sizeof(struct mblock) + size);

  b->r.class = &mb_class;
  add_tail(&p->inside, &b->r.n);
  b->size = size;
  return b->data;
}

348 349 350 351 352 353 354 355 356
/**
 * mb_allocz - allocate and clear a memory block
 * @p: pool
 * @size: size of the block
 *
 * mb_allocz() allocates memory of a given size, initializes it to
 * zeroes and creates a memory block resource representing this memory
 * chunk in the pool @p.
 *
357
 * Please note that mb_allocz() returns a pointer to the memory
358 359 360
 * chunk, not to the resource, hence you have to free it using
 * mb_free(), not rfree().
 */
361 362 363 364 365 366 367 368
void *
mb_allocz(pool *p, unsigned size)
{
  void *x = mb_alloc(p, size);
  bzero(x, size);
  return x;
}

369 370 371 372 373 374 375
/**
 * mb_realloc - reallocate a memory block
 * @m: memory block
 * @size: new size of the block
 *
 * mb_realloc() changes the size of the memory block @m to a given size.
 * The contents will be unchanged to the minimum of the old and new sizes;
376 377 378
 * newly allocated memory will be uninitialized. Contrary to realloc()
 * behavior, @m must be non-NULL, because the resource pool is inherited
 * from it.
379 380
 *
 * Like mb_alloc(), mb_realloc() also returns a pointer to the memory
381
 * chunk, not to the resource, hence you have to free it using
382 383 384
 * mb_free(), not rfree().
 */
void *
385
mb_realloc(void *m, unsigned size)
386
{
387
  struct mblock *b = SKIP_BACK(struct mblock, data, m);
388

389
  b = xrealloc(b, sizeof(struct mblock) + size);
390
  replace_node(&b->r.n, &b->r.n);
391 392 393 394 395
  b->size = size;
  return b->data;
}


396 397 398 399 400 401
/**
 * mb_free - free a memory block
 * @m: memory block
 *
 * mb_free() frees all memory associated with the block @m.
 */
402 403 404
void
mb_free(void *m)
{
405 406 407
  if (!m)
    return;

408 409 410
  struct mblock *b = SKIP_BACK(struct mblock, data, m);
  rfree(b);
}
411

412 413 414 415 416 417 418 419 420 421 422 423


#define STEP_UP(x) ((x) + (x)/2 + 4)

void
buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size)
{
  unsigned nsize = MIN(*size, need);

  while (nsize < need)
    nsize = STEP_UP(nsize);

424
  *buf = mb_realloc(*buf, nsize * item_size);
425 426
  *size = nsize;
}