You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
6.9 KiB
304 lines
6.9 KiB
/*
|
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <sylvan_config.h>
|
|
|
|
#include <sylvan.h>
|
|
#include <sylvan_common.h>
|
|
|
|
#ifndef cas
|
|
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new)))
|
|
#endif
|
|
|
|
/**
|
|
* Static global variables
|
|
*/
|
|
|
|
llmsset_t nodes;
|
|
|
|
/**
|
|
* Retrieve nodes
|
|
*/
|
|
|
|
llmsset_t
|
|
__sylvan_get_internal_data()
|
|
{
|
|
return nodes;
|
|
}
|
|
|
|
/**
|
|
* Calculate table usage (in parallel)
|
|
*/
|
|
VOID_TASK_IMPL_2(sylvan_table_usage, size_t*, filled, size_t*, total)
|
|
{
|
|
size_t tot = llmsset_get_size(nodes);
|
|
if (filled != NULL) *filled = llmsset_count_marked(nodes);
|
|
if (total != NULL) *total = tot;
|
|
}
|
|
|
|
/**
|
|
* Implementation of garbage collection
|
|
*/
|
|
static int gc_enabled = 1;
|
|
static volatile int gc; // variable used in cas switch to ensure only one gc at a time
|
|
|
|
struct reg_gc_mark_entry
|
|
{
|
|
struct reg_gc_mark_entry *next;
|
|
gc_mark_cb cb;
|
|
int order;
|
|
};
|
|
|
|
static struct reg_gc_mark_entry *gc_mark_register = NULL;
|
|
|
|
void
|
|
sylvan_gc_add_mark(int order, gc_mark_cb cb)
|
|
{
|
|
struct reg_gc_mark_entry *e = (struct reg_gc_mark_entry*)malloc(sizeof(struct reg_gc_mark_entry));
|
|
e->cb = cb;
|
|
e->order = order;
|
|
if (gc_mark_register == NULL || gc_mark_register->order>order) {
|
|
e->next = gc_mark_register;
|
|
gc_mark_register = e;
|
|
return;
|
|
}
|
|
struct reg_gc_mark_entry *f = gc_mark_register;
|
|
for (;;) {
|
|
if (f->next == NULL) {
|
|
e->next = NULL;
|
|
f->next = e;
|
|
return;
|
|
}
|
|
if (f->next->order > order) {
|
|
e->next = f->next;
|
|
f->next = e;
|
|
return;
|
|
}
|
|
f = f->next;
|
|
}
|
|
}
|
|
|
|
static gc_hook_cb gc_hook;
|
|
|
|
void
|
|
sylvan_gc_set_hook(gc_hook_cb new_hook)
|
|
{
|
|
gc_hook = new_hook;
|
|
}
|
|
|
|
void
|
|
sylvan_gc_enable()
|
|
{
|
|
gc_enabled = 1;
|
|
}
|
|
|
|
void
|
|
sylvan_gc_disable()
|
|
{
|
|
gc_enabled = 0;
|
|
}
|
|
|
|
/* Mark hook for cache */
|
|
VOID_TASK_0(sylvan_gc_mark_cache)
|
|
{
|
|
/* We simply clear the cache.
|
|
* Alternatively, we could implement for example some strategy
|
|
* where part of the cache is cleared and part is marked
|
|
*/
|
|
cache_clear();
|
|
}
|
|
|
|
/* Default hook */
|
|
|
|
size_t
|
|
next_size(size_t n)
|
|
{
|
|
#if SYLVAN_SIZE_FIBONACCI
|
|
size_t f1=1, f2=1;
|
|
for (;;) {
|
|
f2 += f1;
|
|
if (f2 > n) return f2;
|
|
f1 += f2;
|
|
if (f1 > n) return f1;
|
|
}
|
|
#else
|
|
return n*2;
|
|
#endif
|
|
}
|
|
|
|
VOID_TASK_IMPL_0(sylvan_gc_aggressive_resize)
|
|
{
|
|
/**
|
|
* Always resize when gc called
|
|
*/
|
|
size_t max_size = llmsset_get_max_size(nodes);
|
|
size_t size = llmsset_get_size(nodes);
|
|
if (size < max_size) {
|
|
size_t new_size = next_size(size);
|
|
if (new_size > max_size) new_size = max_size;
|
|
llmsset_set_size(nodes, new_size);
|
|
size_t cache_size = cache_getsize();
|
|
size_t cache_max = cache_getmaxsize();
|
|
if (cache_size < cache_max) {
|
|
new_size = next_size(cache_size);
|
|
if (new_size > cache_max) new_size = cache_max;
|
|
cache_setsize(new_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID_TASK_IMPL_0(sylvan_gc_default_hook)
|
|
{
|
|
/**
|
|
* Default behavior:
|
|
* if we can resize the nodes set, and if we use more than 50%, then increase size
|
|
*/
|
|
size_t max_size = llmsset_get_max_size(nodes);
|
|
size_t size = llmsset_get_size(nodes);
|
|
if (size < max_size) {
|
|
size_t marked = llmsset_count_marked(nodes);
|
|
if (marked*2 > size) {
|
|
size_t new_size = next_size(size);
|
|
if (new_size > max_size) new_size = max_size;
|
|
llmsset_set_size(nodes, new_size);
|
|
size_t cache_size = cache_getsize();
|
|
size_t cache_max = cache_getmaxsize();
|
|
if (cache_size < cache_max) {
|
|
new_size = next_size(cache_size);
|
|
if (new_size > cache_max) new_size = cache_max;
|
|
cache_setsize(new_size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID_TASK_0(sylvan_gc_call_hook)
|
|
{
|
|
// call hook function (resizing, reordering, etc)
|
|
WRAP(gc_hook);
|
|
}
|
|
|
|
VOID_TASK_0(sylvan_gc_rehash)
|
|
{
|
|
// rehash marked nodes
|
|
llmsset_rehash(nodes);
|
|
}
|
|
|
|
VOID_TASK_0(sylvan_gc_destroy_unmarked)
|
|
{
|
|
llmsset_destroy_unmarked(nodes);
|
|
}
|
|
|
|
VOID_TASK_0(sylvan_gc_go)
|
|
{
|
|
sylvan_stats_count(SYLVAN_GC_COUNT);
|
|
sylvan_timer_start(SYLVAN_GC);
|
|
|
|
// clear hash array
|
|
llmsset_clear(nodes);
|
|
|
|
// call mark functions, hook and rehash
|
|
struct reg_gc_mark_entry *e = gc_mark_register;
|
|
while (e != NULL) {
|
|
WRAP(e->cb);
|
|
e = e->next;
|
|
}
|
|
|
|
sylvan_timer_stop(SYLVAN_GC);
|
|
}
|
|
|
|
/* Perform garbage collection */
|
|
VOID_TASK_IMPL_0(sylvan_gc)
|
|
{
|
|
if (!gc_enabled) return;
|
|
if (cas(&gc, 0, 1)) {
|
|
NEWFRAME(sylvan_gc_go);
|
|
gc = 0;
|
|
} else {
|
|
/* wait for new frame to appear */
|
|
while (*(Task* volatile*)&(lace_newframe.t) == 0) {}
|
|
lace_yield(__lace_worker, __lace_dq_head);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Package init and quit functions
|
|
*/
|
|
void
|
|
sylvan_init_package(size_t tablesize, size_t maxsize, size_t cachesize, size_t max_cachesize)
|
|
{
|
|
if (tablesize > maxsize) tablesize = maxsize;
|
|
if (cachesize > max_cachesize) cachesize = max_cachesize;
|
|
|
|
if (maxsize > 0x000003ffffffffff) {
|
|
fprintf(stderr, "sylvan_init_package error: tablesize must be <= 42 bits!\n");
|
|
exit(1);
|
|
}
|
|
|
|
nodes = llmsset_create(tablesize, maxsize);
|
|
cache_create(cachesize, max_cachesize);
|
|
|
|
gc = 0;
|
|
#if SYLVAN_AGGRESSIVE_RESIZE
|
|
gc_hook = TASK(sylvan_gc_aggressive_resize);
|
|
#else
|
|
gc_hook = TASK(sylvan_gc_default_hook);
|
|
#endif
|
|
sylvan_gc_add_mark(10, TASK(sylvan_gc_mark_cache));
|
|
sylvan_gc_add_mark(19, TASK(sylvan_gc_destroy_unmarked));
|
|
sylvan_gc_add_mark(20, TASK(sylvan_gc_call_hook));
|
|
sylvan_gc_add_mark(30, TASK(sylvan_gc_rehash));
|
|
|
|
LACE_ME;
|
|
sylvan_stats_init();
|
|
}
|
|
|
|
struct reg_quit_entry
|
|
{
|
|
struct reg_quit_entry *next;
|
|
quit_cb cb;
|
|
};
|
|
|
|
static struct reg_quit_entry *quit_register = NULL;
|
|
|
|
void
|
|
sylvan_register_quit(quit_cb cb)
|
|
{
|
|
struct reg_quit_entry *e = (struct reg_quit_entry*)malloc(sizeof(struct reg_quit_entry));
|
|
e->next = quit_register;
|
|
e->cb = cb;
|
|
quit_register = e;
|
|
}
|
|
|
|
void
|
|
sylvan_quit()
|
|
{
|
|
while (quit_register != NULL) {
|
|
struct reg_quit_entry *e = quit_register;
|
|
quit_register = e->next;
|
|
e->cb();
|
|
free(e);
|
|
}
|
|
|
|
while (gc_mark_register != NULL) {
|
|
struct reg_gc_mark_entry *e = gc_mark_register;
|
|
gc_mark_register = e->next;
|
|
free(e);
|
|
}
|
|
|
|
cache_free();
|
|
llmsset_free(nodes);
|
|
}
|