414 lines
9.4 KiB
C
414 lines
9.4 KiB
C
|
/* gc.c -- trivial single-threaded stop-world non-moving mark-sweep collector
|
||
|
**
|
||
|
** Copyright (c) 2008 Ian Piumarta
|
||
|
** All Rights Reserved
|
||
|
**
|
||
|
** Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
** copy of this software and associated documentation files (the 'Software'),
|
||
|
** to deal in the Software without restriction, including without limitation
|
||
|
** the rights to use, copy, modify, merge, publish, distribute, and/or sell
|
||
|
** copies of the Software, and to permit persons to whom the Software is
|
||
|
** furnished to do so, provided that the above copyright notice(s) and this
|
||
|
** permission notice appear in all copies of the Software. Inclusion of the
|
||
|
** the above copyright notice(s) and this permission notice in supporting
|
||
|
** documentation would be appreciated but is not required.
|
||
|
**
|
||
|
** THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
|
||
|
**
|
||
|
** Last edited: 2008-10-20 19:45:22 by piumarta on ubuntu.piumarta.com
|
||
|
*/
|
||
|
|
||
|
#include "gc.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#define ALIGN sizeof(long)
|
||
|
#define QUANTUM 32768
|
||
|
#define ALLOCS_PER_GC 10000
|
||
|
|
||
|
#define VERBOSE 0
|
||
|
|
||
|
typedef struct _gcheader
|
||
|
{
|
||
|
union {
|
||
|
int flags;
|
||
|
struct {
|
||
|
unsigned int used : 1;
|
||
|
unsigned int atom : 1;
|
||
|
unsigned int mark : 1;
|
||
|
};
|
||
|
};
|
||
|
struct _gcheader *prev;
|
||
|
struct _gcheader *next;
|
||
|
size_t size;
|
||
|
} gcheader;
|
||
|
|
||
|
static inline void *hdr2ptr(gcheader *hdr) { return (void *)(hdr + 1); }
|
||
|
static inline gcheader *ptr2hdr(void *ptr) { return (gcheader *)ptr - 1; }
|
||
|
|
||
|
static gcheader gcbase= { { -1 }, &gcbase, &gcbase, 0 };
|
||
|
static gcheader *gcnext= &gcbase;
|
||
|
|
||
|
static int gcCount= ALLOCS_PER_GC;
|
||
|
|
||
|
void *GC_malloc(size_t lbs)
|
||
|
{
|
||
|
gcheader *hdr, *org;
|
||
|
if (!gcCount--) {
|
||
|
gcCount= ALLOCS_PER_GC;
|
||
|
GC_gcollect();
|
||
|
}
|
||
|
org= hdr= gcnext;
|
||
|
lbs= (lbs + ALIGN-1) & ~(ALIGN-1);
|
||
|
#if VERBOSE > 1
|
||
|
printf("malloc %d\n", lbs);
|
||
|
#endif
|
||
|
again:
|
||
|
#if VERBOSE > 3
|
||
|
{
|
||
|
gcheader *h= gcnext;
|
||
|
do {
|
||
|
printf(" %2d %p <- %p -> %p = %x\n", h->flags, h->prev, h, h->next, h->size);
|
||
|
h= h->next;
|
||
|
} while (h != gcnext);
|
||
|
}
|
||
|
#endif
|
||
|
do {
|
||
|
#if VERBOSE > 2
|
||
|
printf("? %2d %p <- %p -> %p = %x\n", hdr->flags, hdr->prev, hdr, hdr->next, hdr->size);
|
||
|
#endif
|
||
|
if ((!hdr->used) && (hdr->size >= lbs))
|
||
|
{
|
||
|
void *mem;
|
||
|
if (hdr->size >= lbs + sizeof(gcheader) + ALIGN)
|
||
|
{
|
||
|
gcheader *ins= (gcheader *)(hdr2ptr(hdr) + lbs);
|
||
|
ins->flags= 0;
|
||
|
ins->prev= hdr;
|
||
|
ins->next= hdr->next;
|
||
|
ins->size= hdr->size - lbs - sizeof(gcheader);
|
||
|
hdr->next->prev= ins;
|
||
|
hdr->next= ins;
|
||
|
hdr->size= lbs;
|
||
|
}
|
||
|
hdr->used= 1;
|
||
|
gcnext= hdr->next;
|
||
|
#if VERBOSE > 2
|
||
|
printf("MALLOC %p\n", hdr);
|
||
|
#endif
|
||
|
mem= hdr2ptr(hdr);
|
||
|
memset(mem, 0, hdr->size);
|
||
|
return mem;
|
||
|
}
|
||
|
hdr= hdr->next;
|
||
|
} while (hdr != org);
|
||
|
{
|
||
|
size_t incr= QUANTUM;
|
||
|
size_t req= sizeof(gcheader) + lbs;
|
||
|
while (incr <= req) incr *= 2;
|
||
|
hdr= (gcheader *)sbrk(incr);
|
||
|
if (hdr != (gcheader *)-1)
|
||
|
{
|
||
|
hdr->flags= 0;
|
||
|
hdr->next= &gcbase;
|
||
|
hdr->prev= gcbase.prev;
|
||
|
hdr->prev->next= hdr;
|
||
|
gcbase.prev= hdr;
|
||
|
hdr->size= incr - sizeof(gcheader);
|
||
|
#if VERBOSE
|
||
|
printf("extend by %d at %p\n", hdr->size, hdr);
|
||
|
#endif
|
||
|
goto again;
|
||
|
}
|
||
|
}
|
||
|
printf("out of memory\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void *GC_malloc_atomic(size_t lbs)
|
||
|
{
|
||
|
void *mem= GC_malloc(lbs);
|
||
|
ptr2hdr(mem)->atom= 1;
|
||
|
return mem;
|
||
|
}
|
||
|
|
||
|
void *GC_realloc(void *ptr, size_t lbs)
|
||
|
{
|
||
|
gcheader *hdr= ptr2hdr(ptr);
|
||
|
void *mem;
|
||
|
if (lbs <= hdr->size) return ptr;
|
||
|
mem= GC_malloc(lbs);
|
||
|
memcpy(mem, ptr, hdr->size);
|
||
|
ptr2hdr(mem)->atom= hdr->atom;
|
||
|
GC_free(ptr);
|
||
|
return mem;
|
||
|
}
|
||
|
|
||
|
gcheader *GC_freeHeader(gcheader *hdr)
|
||
|
{
|
||
|
#if VERBOSE > 1
|
||
|
printf("FREE %p\n", hdr);
|
||
|
#endif
|
||
|
hdr->flags= 0;
|
||
|
if ((!hdr->prev->flags) && (hdr2ptr(hdr->prev) + hdr->prev->size == hdr))
|
||
|
{
|
||
|
#if VERBOSE > 2
|
||
|
printf("COALESCE PREV %p\n", hdr->prev);
|
||
|
#endif
|
||
|
hdr->prev->next= hdr->next;
|
||
|
hdr->next->prev= hdr->prev;
|
||
|
hdr->prev->size += sizeof(gcheader) + hdr->size;
|
||
|
hdr= hdr->prev;
|
||
|
}
|
||
|
if ((!hdr->next->used) && (hdr2ptr(hdr) + hdr->size == hdr->next))
|
||
|
{
|
||
|
#if VERBOSE > 2
|
||
|
printf("COALESCE NEXT %p\n", hdr->next);
|
||
|
#endif
|
||
|
hdr->size += sizeof(gcheader) + hdr->next->size;
|
||
|
hdr->next= hdr->next->next;
|
||
|
hdr->next->prev= hdr;
|
||
|
}
|
||
|
#if VERBOSE > 3
|
||
|
{
|
||
|
gcheader *h= &gcbase;
|
||
|
do {
|
||
|
printf(" %2d %p <- %p -> %p = %x\n", h->flags, h->prev, h, h->next, h->size);
|
||
|
h= h->next;
|
||
|
} while (h != &gcbase);
|
||
|
}
|
||
|
#endif
|
||
|
return hdr;
|
||
|
}
|
||
|
|
||
|
void GC_free(void *ptr)
|
||
|
{
|
||
|
gcnext= GC_freeHeader(ptr2hdr(ptr));
|
||
|
}
|
||
|
|
||
|
void GC_default_mark_function(void *ptr)
|
||
|
{
|
||
|
gcheader *hdr= ptr2hdr(ptr);
|
||
|
void **pos= ptr;
|
||
|
void **lim= hdr2ptr(hdr) + hdr->size - sizeof(void *);
|
||
|
while (pos <= lim)
|
||
|
{
|
||
|
void *field= *pos;
|
||
|
if (field && !((long)field & 1))
|
||
|
GC_mark(field);
|
||
|
++pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GC_mark_function_t GC_mark_function= GC_default_mark_function;
|
||
|
|
||
|
void GC_mark(void *ptr)
|
||
|
{
|
||
|
gcheader *hdr= ptr2hdr(ptr);
|
||
|
#if VERBOSE > 2
|
||
|
printf("mark? %p %d\n", hdr, hdr->flags);
|
||
|
#endif
|
||
|
if (!hdr->mark) {
|
||
|
hdr->mark= 1;
|
||
|
if (!hdr->atom)
|
||
|
GC_mark_function(ptr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GC_free_function_t GC_free_function= 0;
|
||
|
|
||
|
void GC_sweep(void)
|
||
|
{
|
||
|
gcheader *hdr= gcbase.next;
|
||
|
do {
|
||
|
#if VERBOSE > 2
|
||
|
printf("sweep? %p %d\n", hdr, hdr->flags);
|
||
|
#endif
|
||
|
if (hdr->flags)
|
||
|
{
|
||
|
if (hdr->mark)
|
||
|
hdr->mark= 0;
|
||
|
else {
|
||
|
if (GC_free_function) GC_free_function(hdr2ptr(hdr));
|
||
|
hdr= GC_freeHeader(hdr);
|
||
|
}
|
||
|
}
|
||
|
hdr= hdr->next;
|
||
|
} while (hdr != &gcbase);
|
||
|
gcnext= gcbase.next;
|
||
|
}
|
||
|
|
||
|
static void ***roots= 0;
|
||
|
static size_t numRoots= 0;
|
||
|
static size_t maxRoots= 0;
|
||
|
|
||
|
struct GC_StackRoot *GC_stack_roots= 0;
|
||
|
|
||
|
void GC_add_root(void *root)
|
||
|
{
|
||
|
if (numRoots == maxRoots)
|
||
|
roots= maxRoots
|
||
|
? realloc(roots, sizeof(roots[0]) * (maxRoots *= 2))
|
||
|
: malloc ( sizeof(roots[0]) * (maxRoots= 128));
|
||
|
roots[numRoots++]= (void **)root;
|
||
|
}
|
||
|
|
||
|
void GC_delete_root(void *root)
|
||
|
{
|
||
|
int i;
|
||
|
for (i= 0; i < numRoots; ++i)
|
||
|
if (roots[i] == (void **)root)
|
||
|
break;
|
||
|
if (i < numRoots)
|
||
|
{
|
||
|
memmove(roots + i, roots + i + 1, sizeof(roots[0]) * (numRoots - i));
|
||
|
--numRoots;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GC_gcollect(void)
|
||
|
{
|
||
|
int i;
|
||
|
struct GC_StackRoot *sr;
|
||
|
for (i= 0; i < numRoots; ++i) if (*roots[i]) GC_mark(*roots[i]);
|
||
|
for (sr= GC_stack_roots; sr; sr= sr->next) if (*(sr->root)) GC_mark(*(sr->root));
|
||
|
GC_sweep();
|
||
|
}
|
||
|
|
||
|
size_t GC_count_objects(void)
|
||
|
{
|
||
|
gcheader *hdr= gcbase.next;
|
||
|
size_t count= 0;
|
||
|
do {
|
||
|
if (hdr->flags)
|
||
|
++count;
|
||
|
hdr= hdr->next;
|
||
|
} while (hdr != &gcbase);
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
size_t GC_count_bytes(void)
|
||
|
{
|
||
|
gcheader *hdr= gcbase.next;
|
||
|
size_t count= 0;
|
||
|
do {
|
||
|
if (hdr->flags)
|
||
|
count += hdr->size;
|
||
|
hdr= hdr->next;
|
||
|
} while (hdr != &gcbase);
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#define RAND(N) (1 + (int)((float)N * (rand() / (RAND_MAX + 1.0))))
|
||
|
|
||
|
struct cell { int tag; struct cell *next; };
|
||
|
|
||
|
void *mklist(int n)
|
||
|
{
|
||
|
struct cell *cell;
|
||
|
if (!n) return 0;
|
||
|
cell= GC_malloc(8);
|
||
|
cell->tag= n << 1 | 1;
|
||
|
cell->next= mklist(n - 1);
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
void delist(struct cell *cell)
|
||
|
{
|
||
|
if (cell && cell->next && cell->next->next) {
|
||
|
cell->next= cell->next->next;
|
||
|
delist(cell->next->next);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
int i, j;
|
||
|
void *a, *b, *c, *d, *e;
|
||
|
for (i= 0; i < 1000000; ++i) {
|
||
|
#if !VERBOSE
|
||
|
# define printf(...)
|
||
|
#endif
|
||
|
//#define GC_malloc malloc
|
||
|
//#define GC_free free
|
||
|
a= GC_malloc(RAND(1)); printf("%p\n", a);
|
||
|
b= GC_malloc(RAND(10)); printf("%p\n", b);
|
||
|
c= GC_malloc(RAND(100)); printf("%p\n", c);
|
||
|
d= GC_malloc(RAND(1000)); printf("%p\n", d);
|
||
|
e= GC_malloc(RAND(10000)); printf("%p\n", e);
|
||
|
GC_free(a);
|
||
|
GC_free(b);
|
||
|
// GC_free(c);
|
||
|
GC_free(d);
|
||
|
GC_free(e);
|
||
|
a= GC_malloc(RAND(100)); printf("%p\n", a);
|
||
|
b= GC_malloc(RAND(200)); printf("%p\n", b);
|
||
|
c= GC_malloc(RAND(300)); printf("%p\n", c);
|
||
|
d= GC_malloc(RAND(400)); printf("%p\n", d);
|
||
|
e= GC_malloc(RAND(500)); printf("%p\n", e);
|
||
|
GC_free(e);
|
||
|
GC_free(d);
|
||
|
// GC_free(c);
|
||
|
GC_free(b);
|
||
|
GC_free(a);
|
||
|
a= GC_malloc(RAND(4)); printf("%p\n", a);
|
||
|
b= GC_malloc(RAND(16)); printf("%p\n", b);
|
||
|
c= GC_malloc(RAND(64)); printf("%p\n", c);
|
||
|
d= GC_malloc(RAND(256)); printf("%p\n", d);
|
||
|
e= GC_malloc(RAND(1024)); printf("%p\n", e);
|
||
|
GC_free(e);
|
||
|
GC_free(b);
|
||
|
// GC_free(c);
|
||
|
GC_free(d);
|
||
|
GC_free(a);
|
||
|
a= GC_malloc(RAND(713)); printf("%p\n", a);
|
||
|
b= GC_malloc(RAND(713)); printf("%p\n", b);
|
||
|
c= GC_malloc(RAND(713)); printf("%p\n", c);
|
||
|
d= GC_malloc(RAND(713)); printf("%p\n", d);
|
||
|
e= GC_malloc(RAND(713)); printf("%p\n", e);
|
||
|
GC_free(a);
|
||
|
GC_free(c);
|
||
|
// GC_free(e);
|
||
|
GC_free(d);
|
||
|
GC_free(b);
|
||
|
#undef printf
|
||
|
if (i % 100000 == 0) printf("alive: %d bytes in %d objects\n", GC_count_bytes(), GC_count_objects());
|
||
|
GC_sweep();
|
||
|
if (i % 100000 == 0) printf("alive: %d bytes in %d objects\n", GC_count_bytes(), GC_count_objects());
|
||
|
}
|
||
|
{
|
||
|
GC_PROTECT(a);
|
||
|
for (i= 0; i < 100; ++i) {
|
||
|
for (j= 0; j < 100; ++j) {
|
||
|
a= mklist(2000);
|
||
|
delist(a);
|
||
|
#if VERBOSE
|
||
|
{
|
||
|
struct cell *c= a;
|
||
|
printf("----\n");
|
||
|
while (c) {
|
||
|
printf("%p %d %p\n", c, c->tag >> 1, c->next);
|
||
|
c= c->next;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
GC_gcollect();
|
||
|
}
|
||
|
GC_UNPROTECT(a);
|
||
|
}
|
||
|
printf("alive: %d bytes in %d objects\n", GC_count_bytes(), GC_count_objects());
|
||
|
GC_sweep();
|
||
|
printf("alive: %d bytes in %d objects\n", GC_count_bytes(), GC_count_objects());
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif
|