nitg & lib: intro `Finalizable` to be called when an object is freed
authorAlexis Laferrière <alexis.laf@xymus.net>
Fri, 8 Aug 2014 23:18:20 +0000 (19:18 -0400)
committerAlexis Laferrière <alexis.laf@xymus.net>
Tue, 12 Aug 2014 19:46:04 +0000 (15:46 -0400)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

clib/gc_chooser.c
clib/gc_chooser.h
lib/standard/gc.nit
src/abstract_compiler.nit
src/global_compiler.nit
src/model/model.nit
src/rapid_type_analysis.nit
src/separate_compiler.nit
src/separate_erasure_compiler.nit

index ca06552..f40a23e 100644 (file)
@@ -120,3 +120,11 @@ void initialize_gc_option(void) {
                default: break; /* Nothing */
        }
 }
+
+void gc_register_finalizer(void* obj) {
+#ifdef WITH_LIBGC
+       GC_register_finalizer(obj, &gc_finalize, NULL, NULL, NULL);
+#endif
+}
+
+void __attribute__((weak)) gc_finalize(void *obj, void* client_data) {}
index 4d1d03a..fa395b0 100644 (file)
@@ -20,4 +20,7 @@ void *nit_raw_alloc(size_t); /* allocate raw memory to store a raw stram of byte
 void nit_gcollect(void); /* force a garbage collection */
 void initialize_gc_option(void); /* Select the wanted GC using envvar `NIT_GC_OPTION` */
 
+void gc_set_finializer(void*); /* Tag a pointer for finalization */
+void gc_finalize(void*, void*); /* Finalize a pointer, implemented in the generated code. */
+
 #endif
index 1ffab48..acee1f6 100644 (file)
@@ -17,3 +17,22 @@ redef class Sys
        # Initiate a garbage collection
        fun force_garbage_collection is intern
 end
+
+# An object needing finalization
+#
+# Sub-classes of `Finalizable` must no have cycles, or else they will not be
+# liberated. For this reason, it is recommended to sub-class `Finalizable`
+# only on simple objects directly managing a limited resource. This use case
+# is common when wrapping an extern instance with a standard object.
+class Finalizable
+
+       # Liberate any resources held by `self` before the memory holding `self` is freed
+       #
+       # This method is invoked by the GC during a garbage collection when `self`
+       # is no longer referenced. It can also be called by the user. Its implementation
+       # must be planned accordingly and ensure that it may be invoked more than once.
+       #
+       # The object are finialized in a topological order, it is safe for this method
+       # to use attributes of this instances.
+       fun finalize do end
+end
index 0ff1e22..d83d574 100644 (file)
@@ -613,6 +613,25 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
 """
        end
 
+       fun compile_finalizer_function
+       do
+               var finalizable_type = mainmodule.finalizable_type
+               if finalizable_type == null then return
+
+               var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
+
+               if finalize_meth == null then
+                       modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
+                       return
+               end
+
+               var v = self.new_visitor
+               v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
+               var recv = v.new_expr("obj", finalizable_type)
+               v.send(finalize_meth, [recv])
+               v.add "\}"
+       end
+
        # Generate the main C function.
        # This function:
        #       * allocate the Sys object if it exists
@@ -1259,6 +1278,17 @@ abstract class AbstractCompilerVisitor
        # Generate a alloc-instance + init-attributes
        fun init_instance(mtype: MClassType): RuntimeVariable is abstract
 
+       # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
+       fun set_finalizer(recv: RuntimeVariable)
+       do
+               var mtype = recv.mtype
+               var finalizable_type = compiler.mainmodule.finalizable_type
+               if finalizable_type != null and not mtype.need_anchor and
+                  mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
+                       add "gc_register_finalizer({recv});"
+               end
+       end
+
        # Generate an integer value
        fun int_instance(value: Int): RuntimeVariable
        do
index 322fcda..3f8b422 100644 (file)
@@ -244,6 +244,7 @@ class GlobalCompiler
                v.add("{res}->classid = {self.classid(mtype)};")
 
                self.generate_init_attr(v, res, mtype)
+               v.set_finalizer res
                v.add("return {res};")
                v.add("\}")
        end
index e2f853a..cc6ca95 100644 (file)
@@ -236,6 +236,13 @@ redef class MModule
                return get_primitive_class("Sys").mclass_type
        end
 
+       fun finalizable_type: nullable MClassType
+       do
+               var clas = self.model.get_mclasses_by_name("Finalizable")
+               if clas == null then return null
+               return get_primitive_class("Finalizable").mclass_type
+       end
+
        # Force to get the primitive class named `name` or abort
        fun get_primitive_class(name: String): MClass
        do
index bb11a4c..2a4e8f2 100644 (file)
@@ -196,6 +196,12 @@ class RapidTypeAnalysis
                        add_send(maintype, mainprop)
                end
 
+               var finalizable_type = mainmodule.finalizable_type
+               if finalizable_type != null then
+                       var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
+                       if finalize_meth != null then add_send(finalizable_type, finalize_meth)
+               end
+
                # Force primitive types
                force_alive("Bool")
                force_alive("Int")
index 273451f..aab5468 100644 (file)
@@ -107,6 +107,7 @@ redef class ModelBuilder
                compiler.new_file("{mainmodule.name}.main")
                compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
+               compiler.compile_finalizer_function
 
                # compile methods
                for m in mainmodule.in_importation.greaters do
@@ -765,6 +766,7 @@ class SeparateCompiler
                        v.require_declaration("class_{c_name}")
                        v.add("{res}->class = &class_{c_name};")
                        self.generate_init_attr(v, res, mtype)
+                       v.set_finalizer res
                        v.add("return {res};")
                end
                v.add("\}")
index a60cccd..f811f3b 100644 (file)
@@ -339,6 +339,7 @@ class SeparateErasureCompiler
                        v.require_declaration("class_{c_name}")
                        v.add("{res}->class = &class_{c_name};")
                        self.generate_init_attr(v, res, mtype)
+                       v.set_finalizer res
                        v.add("return {res};")
                end
                v.add("\}")