ni: adds global references to Nit objects from C code
authorAlexis Laferrière <alexis.laf@xymus.net>
Thu, 1 Mar 2012 23:29:06 +0000 (18:29 -0500)
committerAlexis Laferrière <alexis.laf@xymus.net>
Thu, 12 Apr 2012 19:38:15 +0000 (15:38 -0400)
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 <alexis.laf@xymus.net>

clib/gc.c
clib/nit_common.h
clib/nit_main.c
src/native_interface/frontier.nit

index 2cb8675..ad051d9 100644 (file)
--- 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++) {
index b5d8252..1652ea9 100644 (file)
@@ -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
index 6f7daba..239eb82 100644 (file)
@@ -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 --;
+}
index b972e18..6fa0295 100644 (file)
@@ -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