alloc.c
author Mathieu Lacage <mathieu.lacage@alcmeon.com>
Sat, 23 Feb 2013 20:59:51 +0100
changeset 656 e4817d48962f
parent 596 12c17444489a
permissions -rw-r--r--
basic test for tls

#include "alloc.h"
#include "vdl-mem.h"
#include "system.h"
#include <sys/mman.h>

#ifdef HAVE_VALGRIND_H
# include "valgrind/valgrind.h"
# include "valgrind/memcheck.h"
# define REPORT_MALLOC(buffer, size) \
  VALGRIND_MALLOCLIKE_BLOCK (buffer,size, 0, 0)
# define REPORT_FREE(buffer) \
  VALGRIND_FREELIKE_BLOCK (buffer, 0)
# define MARK_DEFINED(buffer, size)				\
  VALGRIND_MAKE_MEM_DEFINED(buffer, size)
# define MARK_UNDEFINED(buffer, size)			\
  VALGRIND_MAKE_MEM_UNDEFINED(buffer, size)
#else
# define REPORT_MALLOC(buffer, size)
# define REPORT_FREE(buffer)
# define MARK_DEFINED(buffer, size)
# define MARK_UNDEFINED(buffer, size)
#endif

struct AllocMmapChunk
{
  uint8_t *buffer;
  uint32_t size;
  uint32_t brk;
  struct AllocMmapChunk *next;
};
struct AllocAvailable
{
  struct AllocAvailable *next;
};


static uint32_t round_to (uint32_t v, uint32_t to)
{
  return (v + (to - (v % to)));
}

static uint32_t chunk_overhead (void)
{
  return round_to (sizeof (struct AllocMmapChunk), 16);
}

static void alloc_chunk (struct Alloc *alloc, uint32_t size)
{
  size = round_to (size, 4096);
  uint8_t *map = system_mmap (0, size, PROT_READ | PROT_WRITE, 
			      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  struct AllocMmapChunk *chunk = (struct AllocMmapChunk*) (map);
  chunk->buffer = map;
  chunk->size = size;
  chunk->brk = chunk_overhead ();
  chunk->next = alloc->chunks;
  alloc->chunks = chunk;
  MARK_UNDEFINED (chunk->buffer + chunk->brk, size - chunk->brk);
}

static uint8_t *alloc_brk (struct Alloc *alloc, uint32_t needed)
{
  struct AllocMmapChunk *tmp;
  for (tmp = alloc->chunks; tmp != 0; tmp = tmp->next)
    {
      if (tmp->size - tmp->brk >= needed)
	{
	  uint8_t *buffer = tmp->buffer + tmp->brk;
	  tmp->brk += needed;
	  return buffer;
	}
    }
  alloc_chunk (alloc, alloc->default_mmap_size);
  return alloc_brk (alloc, needed);
}
static uint8_t size_to_bucket (uint32_t sz)
{
  uint8_t bucket = 0;
  uint32_t size = sz;
  size--;
  while (size > 7)
    {
      size >>= 1;
      bucket++;
    }
  return bucket;
}
static uint32_t bucket_to_size (uint8_t bucket)
{
  uint32_t size = (1<<(bucket+3));
  return size;
}

static uint8_t *alloc_do_malloc (struct Alloc *alloc, uint32_t size)
{
  if (size < (alloc->default_mmap_size - chunk_overhead ()))
    {
      uint8_t bucket = size_to_bucket (size);
      if (alloc->buckets[bucket] != 0)
	{
	  // fast path.
	  struct AllocAvailable *avail = alloc->buckets[bucket];
	  MARK_DEFINED(avail, sizeof(void*));
	  struct AllocAvailable *next = avail->next;
	  MARK_UNDEFINED(avail, sizeof(void*));
	  alloc->buckets[bucket] = next;
	  REPORT_MALLOC(avail, size);
	  return (uint8_t*)avail;
	}
      // slow path
      struct AllocAvailable *avail = (struct AllocAvailable *) 
	alloc_brk (alloc, bucket_to_size (bucket));
      REPORT_MALLOC(avail, size);
      avail->next = 0;
      return (uint8_t*)avail;
    }
  else
    {
      alloc_chunk (alloc, size + chunk_overhead ());
      uint8_t *buffer = alloc_brk (alloc, size);
      REPORT_MALLOC(buffer, size);
      return buffer;
    }
}

static void alloc_do_free (struct Alloc *alloc, uint8_t *buffer, uint32_t size)
{
  if (size < (alloc->default_mmap_size - chunk_overhead ()))
    {
      // return to bucket list.
      uint8_t bucket = size_to_bucket (size);
      struct AllocAvailable *avail = (struct AllocAvailable *)buffer;
      avail->next = alloc->buckets[bucket];
      alloc->buckets[bucket] = avail;
      REPORT_FREE(buffer);
    }
  else
    {
      struct AllocMmapChunk *tmp, *prev;
      for (tmp = alloc->chunks, prev = 0; tmp != 0; prev = tmp, tmp = tmp->next)
	{
	  if (tmp->buffer == buffer && tmp->size == size)
	    {
	      if (prev == 0)
		{
		  alloc->chunks = tmp->next;
		}
	      else
		{
		  prev->next = tmp->next;
		}
	      REPORT_FREE(buffer);
	      system_munmap (tmp->buffer, tmp->size);
	      return;
	    }
	}
      // this should never happen but it happens in case of a double-free
      REPORT_FREE(buffer);
    }
}

void alloc_initialize (struct Alloc *alloc)
{
  int i;
  alloc->chunks = 0;
  for (i = 0; i < 32; i++)
    {
      alloc->buckets[i] = 0;
    }
  alloc->default_mmap_size = 1<<15;
}
void alloc_destroy (struct Alloc *alloc)
{
  struct AllocMmapChunk *tmp, *next;
  for (tmp = alloc->chunks; tmp != 0; tmp = next)
    {
      next = tmp->next;
      system_munmap (tmp->buffer, tmp->size);
    }
  alloc->chunks = 0;
}

uint8_t *alloc_malloc (struct Alloc *alloc, uint32_t size)
{
  unsigned long *buffer = (unsigned long*) alloc_do_malloc (alloc, 
							    size+1*sizeof(unsigned long));
  buffer[0] = size;
  return (void*)(buffer+1);
}

void alloc_free (struct Alloc *alloc, uint8_t *buffer)
{
  unsigned long *buf = (unsigned long*)buffer;
  unsigned long size = buf[-1];
  vdl_memset (buf, 0x66, size);
  alloc_do_free (alloc, (uint8_t *)(buf-1), size+1*sizeof(unsigned long));
}