From: Alexis Laferrière Date: Thu, 1 Mar 2012 23:29:06 +0000 (-0500) Subject: ni: adds global references to Nit objects from C code X-Git-Tag: v0.5~9^2~17 X-Git-Url: http://nitlanguage.org ni: adds global references to Nit objects from C code Allows to preserve a reference during program execution. It is valid no matter the extern method invoked. Uses a global list to preserve all references explicitly asked to be kept in the program. The user must call Object_incr_ref( Object obj ) to increment the reference count of obj. If it's reference count was at 0, then it will be added to the global list. The same Nit object may be present multiple times in the global list. Only reference incrementation on the same C structure represernting the object will increment the same counter. This algorithm allows for fast increment, no matter how many global references are already registered. Signed-off-by: Alexis Laferrière --- diff --git a/clib/gc.c b/clib/gc.c index 2cb8675..ad051d9 100644 --- a/clib/gc.c +++ b/clib/gc.c @@ -146,6 +146,7 @@ static void GC_collect(void) { struct stack_frame_t *frame = stack_frame_head; GC_static_object *staticObject = staticObjects.top; val_t object; + struct nitni_ref *global_ref; struct nitni_ref_array_link *local_ref_array_link; /* for native interface */ gc_allocation_pointer = gc_heap_pointer; @@ -160,6 +161,16 @@ static void GC_collect(void) { staticObject = staticObject->next; } + /* Process global reference to Nit objects from C code */ + global_ref = nitni_global_ref_list->head; + while (global_ref != NULL) { + object = global_ref->val; + if (!ISNULL(object) && ISOBJ(object)) { + global_ref->val = GC_evacuation((obj_t)object); + } + global_ref = global_ref->next; + } + /* Process function frames (local variables) */ while (frame != NULL) { for (j = 0; j < frame->REG_size; j++) { diff --git a/clib/nit_common.h b/clib/nit_common.h index b5d8252..1652ea9 100644 --- a/clib/nit_common.h +++ b/clib/nit_common.h @@ -127,6 +127,8 @@ extern char ** glob_argv; /* This structure is used to represent every Nit type in extern methods and custom C code. */ struct nitni_ref { val_t val; /* reference to the real Nit object, is kept up-to-date by GC */ + struct nitni_ref *next, *prev; /* adjacent global references in global list */ + int count; /* number of time this global reference has been marked */ }; /* This structure is used by extern methods to keep track of local references to Nit objects */ @@ -146,6 +148,28 @@ extern void nitni_local_ref_add( struct nitni_ref *ref ); /* Clean all references associated to the current (but returning) extern method. */ extern void nitni_local_ref_clean( void ); +/* List of global references from C code to Nit objects */ +/* Instanciated empty at init of Nit system and filled explicitly by user in C code */ +struct nitni_global_ref_list_t { + struct nitni_ref *head, *tail; +}; +extern struct nitni_global_ref_list_t *nitni_global_ref_list; + +/* Initializer of global reference list */ +extern void nitni_global_ref_list_init(); + +/* Intern function to add a global reference to the list */ +extern void nitni_global_ref_add( struct nitni_ref *ref ); + +/* Intern function to remove a global reference from the list */ +extern void nitni_global_ref_remove( struct nitni_ref *ref ); + +/* Increase count on an existing global reference */ +extern void nitni_global_ref_incr( struct nitni_ref *ref ); + +/* Decrease count on an existing global reference */ +extern void nitni_global_ref_decr( struct nitni_ref *ref ); + /* Stack frames. * Are used to: * - store local variables (REGS) of functions diff --git a/clib/nit_main.c b/clib/nit_main.c index 6f7daba..239eb82 100644 --- a/clib/nit_main.c +++ b/clib/nit_main.c @@ -125,6 +125,9 @@ void initialize_gc_option(void) { case nitgc: Nit_gc_init(); break; default: break; /* Nothing */ } + + /* Initialize global references list */ + nitni_global_ref_list_init(); } void prepare_signals(void) { initialize_gc_option(); @@ -198,7 +201,9 @@ void nitni_local_ref_clean( void ) { while ( link != NULL ) { for ( i = 0; i < link->count; i ++ ) { - free( link->reg[ i ] ); + if ( link->reg[i]->count == 0 ) { /* not registered globally */ + free( link->reg[ i ] ); + } } last_link = link; @@ -212,3 +217,58 @@ void nitni_local_ref_clean( void ) { stack_frame_head->nitni_local_ref_head = NULL; } + +struct nitni_global_ref_list_t *nitni_global_ref_list; +void nitni_global_ref_list_init() { + nitni_global_ref_list = (struct nitni_global_ref_list_t*)malloc(sizeof(struct nitni_global_ref_list_t)); + nitni_global_ref_list->head = NULL; + nitni_global_ref_list->tail = NULL; +} + +void nitni_global_ref_add( struct nitni_ref *ref ) { + if ( nitni_global_ref_list->head == NULL ) { + nitni_global_ref_list->head = ref; + nitni_global_ref_list->tail = ref; + + ref->prev = NULL; + } else { + nitni_global_ref_list->tail->next = ref; + ref->prev = nitni_global_ref_list->tail; + } + + ref->next = NULL; +} + +void nitni_global_ref_remove( struct nitni_ref *ref ) { + if ( ref->prev == NULL ) { + nitni_global_ref_list->head = ref->next; + } else { + ref->prev->next = ref->next; + } + + if ( ref->next == NULL ) { + nitni_global_ref_list->tail = ref->prev; + } else { + ref->next->prev = ref->prev; + } +} + +extern void nitni_global_ref_incr( struct nitni_ref *ref ) { + if ( ref->count == 0 ) /* not registered */ + { + /* add to list */ + nitni_global_ref_add( ref ); + } + + ref->count ++; +} + +extern void nitni_global_ref_decr( struct nitni_ref *ref ) { + if ( ref->count == 1 ) /* was last reference */ + { + /* remove from list */ + nitni_global_ref_remove( ref ); + } + + ref->count --; +} diff --git a/src/native_interface/frontier.nit b/src/native_interface/frontier.nit index b972e18..6fa0295 100644 --- a/src/native_interface/frontier.nit +++ b/src/native_interface/frontier.nit @@ -489,6 +489,14 @@ redef class MMType v.body.append( fc.to_writer ) end + # reference incr + var incr_name = "{as_notnull.mangled_name}_incr_ref" + v.header_top.add( "#define {incr_name}( x ) nitni_global_ref_incr( (struct nitni_ref*)(x) )\n" ) + + # reference decr + var decr_name = "{as_notnull.mangled_name}_decr_ref" + v.header_top.add( "#define {decr_name}( x ) nitni_global_ref_decr( (struct nitni_ref*)(x) )\n" ) + v.header_top.add( "#endif\n" ) end end @@ -501,6 +509,7 @@ redef class MMType if uses_nitni_ref then fc.exprs.add( "{var_name} = malloc( sizeof( struct s_{type_name} ) );\n" ) fc.exprs.add( "{var_name}->ref.val = NIT_NULL;\n" ) + fc.exprs.add( "{var_name}->ref.count = 0;\n" ) if stack_it then fc.exprs.add( "nitni_local_ref_add( (struct nitni_ref *){var_name} );\n" ) end