1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 03:30:27 +02:00

Allocate stacks using mmap, and mark them via the thread marker

* libguile/threads.c (thread_mark): Mark the VM stack, if we have one.
  (on_thread_exit): Free the VM stack here.

* libguile/vm.c (make_vm): Allocate the VM stack using mmap, and arrange
  for it to be marked by the thread marker.
  (scm_i_vm_mark_stack, scm_i_vm_free_stack): New internal interfaces.
  (allocate_stack, free_stack): New helpers.
This commit is contained in:
Andy Wingo 2013-11-22 14:41:19 +01:00
parent 7af0c3b395
commit 5f18bc8450
3 changed files with 69 additions and 52 deletions

View file

@ -66,6 +66,7 @@
#include "libguile/init.h"
#include "libguile/scmsigs.h"
#include "libguile/strings.h"
#include "libguile/vm.h"
#include <full-read.h>
@ -88,12 +89,16 @@ thread_mark (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
gc_mark.h.) */
return mark_stack_ptr;
/* Mark T. We could be more precise, but it doesn't matte. */
/* Mark T. We could be more precise, but it doesn't matter. */
for (word = 0; word * sizeof (*addr) < sizeof (*t); word++)
mark_stack_ptr = GC_MARK_AND_PUSH ((void *) addr[word],
mark_stack_ptr, mark_stack_limit,
NULL);
if (t->vp)
mark_stack_ptr = scm_i_vm_mark_stack (t->vp, mark_stack_ptr,
mark_stack_limit);
return mark_stack_ptr;
}
@ -619,6 +624,12 @@ on_thread_exit (void *v)
scm_i_pthread_setspecific (scm_i_thread_key, NULL);
if (t->vp)
{
scm_i_vm_free_stack (t->vp);
t->vp = NULL;
}
#if SCM_USE_PTHREAD_THREADS
GC_unregister_my_thread ();
#endif

View file

@ -26,6 +26,10 @@
#include <string.h>
#include <stdint.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "libguile/bdw-gc.h"
#include <gc/gc_mark.h>
@ -56,12 +60,6 @@ static SCM sym_debug;
/* #define VM_ENABLE_PARANOID_ASSERTIONS */
/* When defined, arrange so that the GC doesn't scan the VM stack beyond its
current SP. This should help avoid excess data retention. See
http://thread.gmane.org/gmane.comp.programming.garbage-collection.boehmgc/3001
for a discussion. */
#define VM_ENABLE_PRECISE_STACK_GC_SCAN
/* Size in SCM objects of the stack reserve. The reserve is used to run
exception handling code in case of a VM stack overflow. */
#define VM_STACK_RESERVE_SIZE 512
@ -712,13 +710,44 @@ typedef SCM (*scm_t_vm_engine) (scm_i_thread *current_thread, struct scm_vm *vp,
static const scm_t_vm_engine vm_engines[SCM_VM_NUM_ENGINES] =
{ vm_regular_engine, vm_debug_engine };
#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
static SCM*
allocate_stack (size_t size)
#define FUNC_NAME "make_vm"
{
void *ret;
/* The GC "kind" for the VM stack. */
static int vm_stack_gc_kind;
if (size >= ((size_t) -1) / sizeof (SCM))
abort ();
size *= sizeof (SCM);
#if HAVE_SYS_MMAN_H
ret = mmap (NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ret == MAP_FAILED)
SCM_SYSERROR;
#else
ret = malloc (size);
if (!ret)
SCM_SYSERROR;
#endif
return (SCM *) ret;
}
#undef FUNC_NAME
static void
free_stack (SCM *stack, size_t size)
{
size *= sizeof (SCM);
#if HAVE_SYS_MMAN_H
munmap (stack, size);
#else
free (stack);
#endif
}
static struct scm_vm *
make_vm (void)
#define FUNC_NAME "make_vm"
@ -729,21 +758,7 @@ make_vm (void)
vp = scm_gc_malloc (sizeof (struct scm_vm), "vm");
vp->stack_size= vm_stack_size;
#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
vp->stack_base = (SCM *)
GC_generic_malloc (vp->stack_size * sizeof (SCM), vm_stack_gc_kind);
/* Keep a pointer to VP so that `vm_stack_mark ()' can know what the stack
top is. */
*vp->stack_base = SCM_PACK_POINTER (vp);
vp->stack_base++;
vp->stack_size--;
#else
vp->stack_base = scm_gc_malloc (vp->stack_size * sizeof (SCM),
"stack-base");
#endif
vp->stack_base = allocate_stack (vp->stack_size);
vp->stack_limit = vp->stack_base + vp->stack_size - VM_STACK_RESERVE_SIZE;
vp->ip = NULL;
vp->sp = vp->stack_base - 1;
@ -757,27 +772,14 @@ make_vm (void)
}
#undef FUNC_NAME
#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
/* Mark the VM stack region between its base and its current top. */
static struct GC_ms_entry *
vm_stack_mark (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
struct GC_ms_entry *mark_stack_limit, GC_word env)
struct GC_ms_entry *
scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
struct GC_ms_entry *mark_stack_limit)
{
GC_word *word;
const struct scm_vm *vm;
/* The first word of the VM stack should contain a pointer to the
corresponding VM. */
vm = * ((struct scm_vm **) addr);
if (vm == NULL
|| (SCM *) addr != vm->stack_base - 1)
/* ADDR must be a pointer to a free-list element, which we must ignore
(see warning in <gc/gc_mark.h>). */
return mark_stack_ptr;
for (word = (GC_word *) vm->stack_base; word <= (GC_word *) vm->sp; word++)
for (word = (GC_word *) vp->stack_base; word <= (GC_word *) vp->sp; word++)
mark_stack_ptr = GC_MARK_AND_PUSH ((* (GC_word **) word),
mark_stack_ptr, mark_stack_limit,
NULL);
@ -785,8 +787,14 @@ vm_stack_mark (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
return mark_stack_ptr;
}
#endif /* VM_ENABLE_PRECISE_STACK_GC_SCAN */
/* Free the VM stack, as this thread is exiting. */
void
scm_i_vm_free_stack (struct scm_vm *vp)
{
free_stack (vp->stack_base, vp->stack_size);
vp->stack_base = vp->stack_limit = NULL;
vp->stack_size = 0;
}
static struct scm_vm *
thread_vm (scm_i_thread *t)
@ -1102,14 +1110,6 @@ scm_bootstrap_vm (void)
vm_builtin_##builtin = scm_i_make_program (vm_builtin_##builtin##_code);
FOR_EACH_VM_BUILTIN (DEFINE_BUILTIN);
#undef DEFINE_BUILTIN
#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
vm_stack_gc_kind =
GC_new_kind (GC_new_free_list (),
GC_MAKE_PROC (GC_new_proc (vm_stack_mark), 0),
0, 1);
#endif
}
void

View file

@ -65,6 +65,12 @@ SCM_API SCM scm_set_default_vm_engine_x (SCM engine);
SCM_API void scm_c_set_vm_engine_x (int engine);
SCM_API void scm_c_set_default_vm_engine_x (int engine);
struct GC_ms_entry;
SCM_INTERNAL struct GC_ms_entry * scm_i_vm_mark_stack (struct scm_vm *,
struct GC_ms_entry *,
struct GC_ms_entry *);
SCM_INTERNAL void scm_i_vm_free_stack (struct scm_vm *vp);
#define SCM_F_VM_CONT_PARTIAL 0x1
#define SCM_F_VM_CONT_REWINDABLE 0x2