add scheme_add_gc_callback() hook for gc notification in a way that lets you describe a foreign function to call without allocating or the call; the allowed protocols are contrained and ad hoc, but it will be enough for GRacket2

This commit is contained in:
Matthew Flatt 2010-09-12 09:20:47 -06:00
parent b601aa1ca3
commit 3e5c7fefdd
14 changed files with 219 additions and 3 deletions

View File

@ -1,5 +1,6 @@
#lang scribble/doc #lang scribble/doc
@(require "utils.ss") @(require "utils.ss"
(for-label ffi/unsafe))
@title[#:tag "im:memoryalloc"]{Memory Allocation} @title[#:tag "im:memoryalloc"]{Memory Allocation}
@ -1032,3 +1033,47 @@ implementations of the memory manager, the result is the same as
moved before it is fixed. With other implementations, an object might moved before it is fixed. With other implementations, an object might
be moved after the fixup process, and the result is the location that be moved after the fixup process, and the result is the location that
the object will have after garbage collection finished.} the object will have after garbage collection finished.}
@function[(Scheme_Object* scheme_add_gc_callback [Scheme_Object* pre_desc]
[Scheme_Object* post_desc])]{
Registers descriptions of foreign functions to be called just before
and just after a garbage collection. The foreign functions must not
allocate garbage-collected memory, and they are called in a way that
does not allocate, which is why @var{pre_desc} and @var{post_desc} are
function descriptions instead of thunks.
A description is a vector of vectors, where each of the inner vectors
describes a single call, and the calls are performed in sequence. Each
call vector starts with a symbol that indicates the protocol of the
foreign function to be called. The following protocols are supported:
@itemlist[
@item{@racket['ptr_ptr_ptr->void] corresponds to @cpp{void
(*)(void*, void*, void*)}.}
@item{@racket['ptr_ptr_ptr_int->void] corresponds to @cpp{void
(*)(void*, void*, void*, int)}.}
@item{@racket['ptr_ptr_float->void] corresponds to @cpp{void
(*)(void*, void*, float)}.}
@item{@racket['ptr_ptr_double->void] corresponds to @cpp{void
(*)(void*, void*, double)}.}
]
After the protocol symbol, the vector should contain a pointer to a
foreign function and then an element for each of the function's
arguments. Pointer values are represented as for the @racket[_pointer]
representation defined by @racketmodname[ffi/unsafe].
The result is a key for use with @cpp{scheme_remove_gc_callback}. If
the key becomes inaccessible, then the callback will be removed
automatically (but beware that the pre-callback will have executed and
the post-callback will not have executed).}
@function[(void scheme_remove_gc_callback [Scheme_Object* key])]{
Removes a garbage-collection callback installed with @cpp{scheme_add_gc_callback}.}

View File

@ -1243,6 +1243,10 @@ static Scheme_Object *foreign_set_cpointer_tag_bang(int argc, Scheme_Object *arg
} }
#undef MYNAME #undef MYNAME
void *scheme_extract_pointer(Scheme_Object *v) {
return SCHEME_FFIANYPTR_VAL(v);
}
/*****************************************************************************/ /*****************************************************************************/
/* Scheme<-->C conversions */ /* Scheme<-->C conversions */

View File

@ -1030,6 +1030,10 @@ ffi_abi sym_to_abi(char *who, Scheme_Object *sym)
return scheme_void; return scheme_void;
} }
void *scheme_extract_pointer(Scheme_Object *v) {
return SCHEME_FFIANYPTR_VAL(v);
}
/*****************************************************************************/ /*****************************************************************************/
/* Scheme<-->C conversions */ /* Scheme<-->C conversions */

View File

@ -202,6 +202,8 @@ EXPORTS
scheme_collect_garbage scheme_collect_garbage
scheme_malloc_immobile_box scheme_malloc_immobile_box
scheme_free_immobile_box scheme_free_immobile_box
scheme_add_gc_callback
scheme_remove_gc_callback
scheme_make_bucket_table scheme_make_bucket_table
scheme_add_to_table scheme_add_to_table
scheme_change_in_table scheme_change_in_table

View File

@ -217,6 +217,8 @@ EXPORTS
GC_fixup_self GC_fixup_self
scheme_malloc_immobile_box scheme_malloc_immobile_box
scheme_free_immobile_box scheme_free_immobile_box
scheme_add_gc_callback
scheme_remove_gc_callback
scheme_make_bucket_table scheme_make_bucket_table
scheme_add_to_table scheme_add_to_table
scheme_change_in_table scheme_change_in_table

View File

@ -219,6 +219,8 @@ GC_fixup
GC_fixup_self GC_fixup_self
scheme_malloc_immobile_box scheme_malloc_immobile_box
scheme_free_immobile_box scheme_free_immobile_box
scheme_add_gc_callback
scheme_remove_gc_callback
scheme_make_bucket_table scheme_make_bucket_table
scheme_add_to_table scheme_add_to_table
scheme_change_in_table scheme_change_in_table

View File

@ -225,6 +225,8 @@ GC_fixup
GC_fixup_self GC_fixup_self
scheme_malloc_immobile_box scheme_malloc_immobile_box
scheme_free_immobile_box scheme_free_immobile_box
scheme_add_gc_callback
scheme_remove_gc_callback
scheme_make_bucket_table scheme_make_bucket_table
scheme_add_to_table scheme_add_to_table
scheme_change_in_table scheme_change_in_table

View File

@ -293,7 +293,7 @@ typedef struct Thread_Local_Variables {
struct Scheme_Hash_Table *place_local_keyword_table_; struct Scheme_Hash_Table *place_local_keyword_table_;
struct Scheme_Hash_Table *place_local_parallel_symbol_table_; struct Scheme_Hash_Table *place_local_parallel_symbol_table_;
struct FFI_Sync_Queue *ffi_sync_queue_; struct FFI_Sync_Queue *ffi_sync_queue_;
/*KPLAKE1*/ struct Scheme_GC_Pre_Post_Callback_Desc *gc_prepost_callback_descs_;
} Thread_Local_Variables; } Thread_Local_Variables;
#if defined(IMPLEMENT_THREAD_LOCAL_VIA_PTHREADS) #if defined(IMPLEMENT_THREAD_LOCAL_VIA_PTHREADS)
@ -588,7 +588,7 @@ XFORM_GC_VARIABLE_STACK_THROUGH_THREAD_LOCAL;
#define place_local_keyword_table XOA (scheme_get_thread_local_variables()->place_local_keyword_table_) #define place_local_keyword_table XOA (scheme_get_thread_local_variables()->place_local_keyword_table_)
#define place_local_parallel_symbol_table XOA (scheme_get_thread_local_variables()->place_local_parallel_symbol_table_) #define place_local_parallel_symbol_table XOA (scheme_get_thread_local_variables()->place_local_parallel_symbol_table_)
#define ffi_sync_queue XOA (scheme_get_thread_local_variables()->ffi_sync_queue_) #define ffi_sync_queue XOA (scheme_get_thread_local_variables()->ffi_sync_queue_)
/*KPLAKE2*/ #define gc_prepost_callback_descs XOA (scheme_get_thread_local_variables()->gc_prepost_callback_descs_)
/* **************************************** */ /* **************************************** */

View File

@ -435,6 +435,9 @@ MZ_EXTERN void *GC_fixup_self(void *p);
MZ_EXTERN void **scheme_malloc_immobile_box(void *p); MZ_EXTERN void **scheme_malloc_immobile_box(void *p);
MZ_EXTERN void scheme_free_immobile_box(void **b); MZ_EXTERN void scheme_free_immobile_box(void **b);
MZ_EXTERN Scheme_Object *scheme_add_gc_callback(Scheme_Object *pre, Scheme_Object *post);
MZ_EXTERN void scheme_remove_gc_callback(Scheme_Object *key);
/*========================================================================*/ /*========================================================================*/
/* hash tables */ /* hash tables */
/*========================================================================*/ /*========================================================================*/

View File

@ -351,6 +351,8 @@ void *(*GC_fixup_self)(void *p);
#endif #endif
void **(*scheme_malloc_immobile_box)(void *p); void **(*scheme_malloc_immobile_box)(void *p);
void (*scheme_free_immobile_box)(void **b); void (*scheme_free_immobile_box)(void **b);
Scheme_Object *(*scheme_add_gc_callback)(Scheme_Object *pre, Scheme_Object *post);
void (*scheme_remove_gc_callback)(Scheme_Object *key);
/*========================================================================*/ /*========================================================================*/
/* hash tables */ /* hash tables */
/*========================================================================*/ /*========================================================================*/

View File

@ -250,6 +250,8 @@
#endif #endif
scheme_extension_table->scheme_malloc_immobile_box = scheme_malloc_immobile_box; scheme_extension_table->scheme_malloc_immobile_box = scheme_malloc_immobile_box;
scheme_extension_table->scheme_free_immobile_box = scheme_free_immobile_box; scheme_extension_table->scheme_free_immobile_box = scheme_free_immobile_box;
scheme_extension_table->scheme_add_gc_callback = scheme_add_gc_callback;
scheme_extension_table->scheme_remove_gc_callback = scheme_remove_gc_callback;
scheme_extension_table->scheme_make_bucket_table = scheme_make_bucket_table; scheme_extension_table->scheme_make_bucket_table = scheme_make_bucket_table;
scheme_extension_table->scheme_add_to_table = scheme_add_to_table; scheme_extension_table->scheme_add_to_table = scheme_add_to_table;
scheme_extension_table->scheme_change_in_table = scheme_change_in_table; scheme_extension_table->scheme_change_in_table = scheme_change_in_table;

View File

@ -250,6 +250,8 @@
#endif #endif
#define scheme_malloc_immobile_box (scheme_extension_table->scheme_malloc_immobile_box) #define scheme_malloc_immobile_box (scheme_extension_table->scheme_malloc_immobile_box)
#define scheme_free_immobile_box (scheme_extension_table->scheme_free_immobile_box) #define scheme_free_immobile_box (scheme_extension_table->scheme_free_immobile_box)
#define scheme_add_gc_callback (scheme_extension_table->scheme_add_gc_callback)
#define scheme_remove_gc_callback (scheme_extension_table->scheme_remove_gc_callback)
#define scheme_make_bucket_table (scheme_extension_table->scheme_make_bucket_table) #define scheme_make_bucket_table (scheme_extension_table->scheme_make_bucket_table)
#define scheme_add_to_table (scheme_extension_table->scheme_add_to_table) #define scheme_add_to_table (scheme_extension_table->scheme_add_to_table)
#define scheme_change_in_table (scheme_extension_table->scheme_change_in_table) #define scheme_change_in_table (scheme_extension_table->scheme_change_in_table)

View File

@ -464,6 +464,10 @@ void scheme_resume_remembered_threads(void);
extern void scheme_check_foreign_work(void); extern void scheme_check_foreign_work(void);
#endif #endif
#ifndef DONT_USE_FOREIGN
extern void *scheme_extract_pointer(Scheme_Object *v);
#endif
void scheme_kickoff_green_thread_time_slice_timer(long usec); void scheme_kickoff_green_thread_time_slice_timer(long usec);
#ifdef UNIX_PROCESSES #ifdef UNIX_PROCESSES

View File

@ -210,6 +210,8 @@ HOOK_SHARED_OK void (*scheme_on_atomic_timeout)(void);
HOOK_SHARED_OK static int atomic_timeout_auto_suspend; HOOK_SHARED_OK static int atomic_timeout_auto_suspend;
HOOK_SHARED_OK static int atomic_timeout_atomic_level; HOOK_SHARED_OK static int atomic_timeout_atomic_level;
THREAD_LOCAL_DECL(struct Scheme_GC_Pre_Post_Callback_Desc *gc_prepost_callback_descs);
ROSYM static Scheme_Object *read_symbol, *write_symbol, *execute_symbol, *delete_symbol, *exists_symbol; ROSYM static Scheme_Object *read_symbol, *write_symbol, *execute_symbol, *delete_symbol, *exists_symbol;
ROSYM static Scheme_Object *client_symbol, *server_symbol; ROSYM static Scheme_Object *client_symbol, *server_symbol;
ROSYM static Scheme_Object *froz_key; ROSYM static Scheme_Object *froz_key;
@ -824,6 +826,7 @@ void scheme_init_thread_places(void) {
buffer_init_size = INIT_TB_SIZE; buffer_init_size = INIT_TB_SIZE;
REGISTER_SO(recycle_cell); REGISTER_SO(recycle_cell);
REGISTER_SO(maybe_recycle_cell); REGISTER_SO(maybe_recycle_cell);
REGISTER_SO(gc_prepost_callback_descs);
} }
void scheme_init_memtrace(Scheme_Env *env) void scheme_init_memtrace(Scheme_Env *env)
@ -7375,6 +7378,141 @@ static Scheme_Object *will_executor_sema(Scheme_Object *w, int *repost)
START_XFORM_SKIP; START_XFORM_SKIP;
#endif #endif
typedef struct Scheme_GC_Pre_Post_Callback_Desc {
/* All pointer fields => allocate with GC_malloc() */
Scheme_Object *boxed_key;
Scheme_Object *pre_desc;
Scheme_Object *post_desc;
struct Scheme_GC_Pre_Post_Callback_Desc *prev;
struct Scheme_GC_Pre_Post_Callback_Desc *next;
} Scheme_GC_Pre_Post_Callback_Desc;
Scheme_Object *scheme_add_gc_callback(Scheme_Object *pre, Scheme_Object *post)
{
Scheme_GC_Pre_Post_Callback_Desc *desc;
Scheme_Object *key, *boxed;
desc = (Scheme_GC_Pre_Post_Callback_Desc *)GC_malloc(sizeof(Scheme_GC_Pre_Post_Callback_Desc));
desc->pre_desc = pre;
desc->post_desc = post;
key = scheme_make_vector(1, scheme_false);
boxed = scheme_make_weak_box(key);
desc->boxed_key = boxed;
desc->next = gc_prepost_callback_descs;
gc_prepost_callback_descs = desc;
return key;
}
void scheme_remove_gc_callback(Scheme_Object *key)
{
Scheme_GC_Pre_Post_Callback_Desc *prev = NULL, *desc;
desc = gc_prepost_callback_descs;
while (desc) {
if (SAME_OBJ(SCHEME_WEAK_BOX_VAL(desc->boxed_key), key)) {
if (prev)
prev->next = desc->next;
else
gc_prepost_callback_descs = desc->next;
if (desc->next)
desc->next->prev = desc->prev;
}
prev = desc;
desc = desc->next;
}
}
typedef void (*gccb_Ptr_Ptr_Ptr_Int_to_Void)(void*, void*, void*, int);
typedef void (*gccb_Ptr_Ptr_Ptr_to_Void)(void*, void*, void*);
typedef void (*gccb_Ptr_Ptr_Float_to_Void)(void*, void*, float);
typedef void (*gccb_Ptr_Ptr_Double_to_Void)(void*, void*, double);
#ifdef DONT_USE_FOREIGN
# define scheme_extract_pointer(x) NULL
#endif
static void run_gc_callbacks(int pre)
XFORM_SKIP_PROC
{
Scheme_GC_Pre_Post_Callback_Desc *prev = NULL, *desc;
Scheme_Object *acts, *act, *protocol;
int j;
desc = gc_prepost_callback_descs;
while (desc) {
if (!SCHEME_WEAK_BOX_VAL(desc->boxed_key)) {
if (prev)
prev->next = desc->next;
else
gc_prepost_callback_descs = desc->next;
if (desc->next)
desc->next->prev = desc->prev;
} else {
if (pre)
acts = desc->pre_desc;
else
acts = desc->post_desc;
for (j = 0; j < SCHEME_VEC_SIZE(acts); j++) {
act = SCHEME_VEC_ELS(acts)[j];
protocol = SCHEME_VEC_ELS(act)[0];
/* The set of suported protocols is arbitary, based on what we've needed
so far. */
if (!strcmp(SCHEME_SYM_VAL(protocol), "ptr_ptr_ptr_int->void")) {
gccb_Ptr_Ptr_Ptr_Int_to_Void proc;
void *a, *b, *c;
int i;
proc = (gccb_Ptr_Ptr_Ptr_Int_to_Void)scheme_extract_pointer(SCHEME_VEC_ELS(act)[1]);
a = scheme_extract_pointer(SCHEME_VEC_ELS(act)[2]);
b = scheme_extract_pointer(SCHEME_VEC_ELS(act)[3]);
c = scheme_extract_pointer(SCHEME_VEC_ELS(act)[4]);
i = SCHEME_INT_VAL(SCHEME_VEC_ELS(act)[5]);
proc(a, b, c, i);
} else if (!strcmp(SCHEME_SYM_VAL(protocol), "ptr_ptr_ptr->void")) {
gccb_Ptr_Ptr_Ptr_to_Void proc;
void *a, *b, *c;
proc = (gccb_Ptr_Ptr_Ptr_to_Void)scheme_extract_pointer(SCHEME_VEC_ELS(act)[1]);
a = scheme_extract_pointer(SCHEME_VEC_ELS(act)[2]);
b = scheme_extract_pointer(SCHEME_VEC_ELS(act)[3]);
c = scheme_extract_pointer(SCHEME_VEC_ELS(act)[4]);
proc(a, b, c);
} else if (!strcmp(SCHEME_SYM_VAL(protocol), "ptr_ptr_float->void")) {
gccb_Ptr_Ptr_Float_to_Void proc;
void *a, *b;
float f;
proc = (gccb_Ptr_Ptr_Float_to_Void)scheme_extract_pointer(SCHEME_VEC_ELS(act)[1]);
a = scheme_extract_pointer(SCHEME_VEC_ELS(act)[2]);
b = scheme_extract_pointer(SCHEME_VEC_ELS(act)[3]);
f = SCHEME_DBL_VAL(SCHEME_VEC_ELS(act)[4]);
proc(a, b, f);
} else if (!strcmp(SCHEME_SYM_VAL(protocol), "ptr_ptr_double->void")) {
gccb_Ptr_Ptr_Double_to_Void proc;
void *a, *b;
double d;
proc = (gccb_Ptr_Ptr_Double_to_Void)scheme_extract_pointer(SCHEME_VEC_ELS(act)[1]);
a = scheme_extract_pointer(SCHEME_VEC_ELS(act)[2]);
b = scheme_extract_pointer(SCHEME_VEC_ELS(act)[3]);
d = SCHEME_DBL_VAL(SCHEME_VEC_ELS(act)[4]);
proc(a, b, d);
}
prev = desc;
}
}
desc = desc->next;
}
}
void scheme_zero_unneeded_rands(Scheme_Thread *p) void scheme_zero_unneeded_rands(Scheme_Thread *p)
{ {
/* Call this procedure before GC or before copying out /* Call this procedure before GC or before copying out
@ -7533,6 +7671,8 @@ static void get_ready_for_GC()
scheme_future_block_until_gc(); scheme_future_block_until_gc();
#endif #endif
run_gc_callbacks(1);
scheme_zero_unneeded_rands(scheme_current_thread); scheme_zero_unneeded_rands(scheme_current_thread);
scheme_clear_modidx_cache(); scheme_clear_modidx_cache();
@ -7601,6 +7741,8 @@ static void done_with_GC()
end_this_gc_time = scheme_get_process_milliseconds(); end_this_gc_time = scheme_get_process_milliseconds();
scheme_total_gc_time += (end_this_gc_time - start_this_gc_time); scheme_total_gc_time += (end_this_gc_time - start_this_gc_time);
run_gc_callbacks(0);
#ifdef MZ_USE_FUTURES #ifdef MZ_USE_FUTURES
scheme_future_continue_after_gc(); scheme_future_continue_after_gc();
#endif #endif