Cut over to new mark buffer data structure

This commit is contained in:
Justin Ethier 2018-06-11 13:30:02 -04:00
parent b4f4c9a182
commit 9e94d4ee46
2 changed files with 73 additions and 15 deletions

78
gc.c
View file

@ -97,6 +97,64 @@ static struct ck_malloc my_allocator = {
.realloc = my_realloc .realloc = my_realloc
}; };
/** Mark buffers
*
* For these, we need a buffer than can grow as needed but that can also be
* used concurrently by both a mutator thread and a collector thread.
*/
static mark_buffer *mark_buffer_init(unsigned initial_size)
{
mark_buffer *mb = malloc(sizeof(mark_buffer));
mb->buf = malloc(sizeof(void *) * initial_size);
mb->buf_len = initial_size;
mb->next = NULL;
return mb;
}
static void *mark_buffer_get(mark_buffer *mb, unsigned i) // TODO: macro?
{
while (i >= mb->buf_len) {
// Not on this page, try the next one
i -= mb->buf_len;
mb = mb->next;
if (mb == NULL) { // Safety check
// For now this is a fatal error, could return NULL instead
fprintf(stderr, "mark_buffer_get ran out of mark buffers, exiting\n");
exit(1);
}
}
return mb->buf[i];
}
static void mark_buffer_set(mark_buffer *mb, unsigned i, void *obj)
{
// Find index i
while (i >= mb->buf_len) {
// Not on this page, try the next one
i -= mb->buf_len;
if (mb->next == NULL) {
// If it does not exist, allocate a new buffer
mb->next = mark_buffer_init(mb->buf_len * 2);
}
mb = mb->next;
}
mb->buf[i] = obj;
}
static void mark_buffer_free(mark_buffer *mb)
{
mark_buffer *next;
while (mb) {
next = mb->next;
free(mb->buf);
free(mb);
mb = next;
}
}
// END mark buffer
#if GC_DEBUG_TRACE #if GC_DEBUG_TRACE
const int NUM_ALLOC_SIZES = 10; const int NUM_ALLOC_SIZES = 10;
static double allocated_size_counts[10] = { static double allocated_size_counts[10] = {
@ -1605,9 +1663,7 @@ void gc_mark_gray(gc_thread_data * thd, object obj)
// Note that ideally this should be a lock-free data structure to make the // Note that ideally this should be a lock-free data structure to make the
// algorithm more efficient. So this code (and the corresponding collector // algorithm more efficient. So this code (and the corresponding collector
// trace code) should be converted at some point. // trace code) should be converted at some point.
thd->mark_buffer = vpbuffer_add(thd->mark_buffer, mark_buffer_set(thd->mark_buffer, thd->last_write, obj);
&(thd->mark_buffer_len),
thd->last_write, obj);
(thd->last_write)++; // Already locked, just do it... (thd->last_write)++; // Already locked, just do it...
} }
} }
@ -1626,10 +1682,7 @@ void gc_mark_gray(gc_thread_data * thd, object obj)
void gc_mark_gray2(gc_thread_data * thd, object obj) void gc_mark_gray2(gc_thread_data * thd, object obj)
{ {
if (is_object_type(obj) && mark(obj) == gc_color_clear) { if (is_object_type(obj) && mark(obj) == gc_color_clear) {
thd->mark_buffer = vpbuffer_add(thd->mark_buffer, mark_buffer_set(thd->mark_buffer, thd->last_write + thd->pending_writes, obj);
&(thd->mark_buffer_len),
(thd->last_write + thd->pending_writes),
obj);
thd->pending_writes++; thd->pending_writes++;
} }
} }
@ -1789,9 +1842,9 @@ void gc_collector_trace()
#if GC_DEBUG_VERBOSE #if GC_DEBUG_VERBOSE
fprintf(stderr, fprintf(stderr,
"gc_mark_black mark buffer %p, last_read = %d last_write = %d\n", "gc_mark_black mark buffer %p, last_read = %d last_write = %d\n",
(m->mark_buffer)[m->last_read], m->last_read, m->last_write); mark_buffer_get(m->mark_buffer, m->last_read), m->last_read, m->last_write);
#endif #endif
gc_mark_black((m->mark_buffer)[m->last_read]); gc_mark_black(mark_buffer_get(m->mark_buffer, m->last_read));
gc_empty_collector_stack(); gc_empty_collector_stack();
(m->last_read)++; // Inc here to prevent off-by-one error (m->last_read)++; // Inc here to prevent off-by-one error
} }
@ -2151,10 +2204,7 @@ void gc_thread_data_init(gc_thread_data * thd, int mut_num, char *stack_base,
thd->pending_writes = 0; thd->pending_writes = 0;
thd->last_write = 0; thd->last_write = 0;
thd->last_read = 0; thd->last_read = 0;
thd->mark_buffer = NULL; thd->mark_buffer = mark_buffer_init(128);
thd->mark_buffer_len = 128;
thd->mark_buffer =
vpbuffer_realloc(thd->mark_buffer, &(thd->mark_buffer_len));
if (pthread_mutex_init(&(thd->heap_lock), NULL) != 0) { if (pthread_mutex_init(&(thd->heap_lock), NULL) != 0) {
fprintf(stderr, "Unable to initialize thread mutex\n"); fprintf(stderr, "Unable to initialize thread mutex\n");
exit(1); exit(1);
@ -2215,7 +2265,7 @@ void gc_thread_data_free(gc_thread_data * thd)
if (thd->moveBuf) if (thd->moveBuf)
free(thd->moveBuf); free(thd->moveBuf);
if (thd->mark_buffer) if (thd->mark_buffer)
free(thd->mark_buffer); mark_buffer_free(thd->mark_buffer);
if (thd->stack_traces) if (thd->stack_traces)
free(thd->stack_traces); free(thd->stack_traces);
if (thd->mutations) { if (thd->mutations) {

View file

@ -246,6 +246,14 @@ typedef enum { STAGE_CLEAR_OR_MARKING, STAGE_TRACING
/** Unallocated memory */ /** Unallocated memory */
#define gc_color_blue 2 #define gc_color_blue 2
/** Mark buffers */
typedef struct mark_buffer_t mark_buffer;
struct mark_buffer_t {
void **buf;
unsigned buf_len;
mark_buffer *next;
};
/** Threading */ /** Threading */
typedef enum { CYC_THREAD_STATE_NEW, CYC_THREAD_STATE_RUNNABLE, typedef enum { CYC_THREAD_STATE_NEW, CYC_THREAD_STATE_RUNNABLE,
CYC_THREAD_STATE_BLOCKED, CYC_THREAD_STATE_BLOCKED_COOPERATING, CYC_THREAD_STATE_BLOCKED, CYC_THREAD_STATE_BLOCKED_COOPERATING,
@ -287,7 +295,7 @@ struct gc_thread_data_t {
int last_write; int last_write;
int last_read; int last_read;
int pending_writes; int pending_writes;
void **mark_buffer; mark_buffer *mark_buffer;
int mark_buffer_len; int mark_buffer_len;
pthread_mutex_t lock; pthread_mutex_t lock;
pthread_mutex_t heap_lock; pthread_mutex_t heap_lock;