From 70d0068c1e2417e644dee459b61ede71a115206d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 8 Aug 2014 19:18:20 -0400 Subject: [PATCH] nitg & lib: intro `Finalizable` to be called when an object is freed MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- clib/gc_chooser.c | 8 ++++++++ clib/gc_chooser.h | 3 +++ lib/standard/gc.nit | 19 +++++++++++++++++++ src/abstract_compiler.nit | 30 ++++++++++++++++++++++++++++++ src/global_compiler.nit | 1 + src/model/model.nit | 7 +++++++ src/rapid_type_analysis.nit | 6 ++++++ src/separate_compiler.nit | 2 ++ src/separate_erasure_compiler.nit | 1 + 9 files changed, 77 insertions(+) diff --git a/clib/gc_chooser.c b/clib/gc_chooser.c index ca06552..f40a23e 100644 --- a/clib/gc_chooser.c +++ b/clib/gc_chooser.c @@ -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) {} diff --git a/clib/gc_chooser.h b/clib/gc_chooser.h index 4d1d03a..fa395b0 100644 --- a/clib/gc_chooser.h +++ b/clib/gc_chooser.h @@ -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 diff --git a/lib/standard/gc.nit b/lib/standard/gc.nit index 1ffab48..acee1f6 100644 --- a/lib/standard/gc.nit +++ b/lib/standard/gc.nit @@ -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 diff --git a/src/abstract_compiler.nit b/src/abstract_compiler.nit index 0ff1e22..d83d574 100644 --- a/src/abstract_compiler.nit +++ b/src/abstract_compiler.nit @@ -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 diff --git a/src/global_compiler.nit b/src/global_compiler.nit index 322fcda..3f8b422 100644 --- a/src/global_compiler.nit +++ b/src/global_compiler.nit @@ -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 diff --git a/src/model/model.nit b/src/model/model.nit index e2f853a..cc6ca95 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -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 diff --git a/src/rapid_type_analysis.nit b/src/rapid_type_analysis.nit index bb11a4c..2a4e8f2 100644 --- a/src/rapid_type_analysis.nit +++ b/src/rapid_type_analysis.nit @@ -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") diff --git a/src/separate_compiler.nit b/src/separate_compiler.nit index 273451f..aab5468 100644 --- a/src/separate_compiler.nit +++ b/src/separate_compiler.nit @@ -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("\}") diff --git a/src/separate_erasure_compiler.nit b/src/separate_erasure_compiler.nit index a60cccd..f811f3b 100644 --- a/src/separate_erasure_compiler.nit +++ b/src/separate_erasure_compiler.nit @@ -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("\}") -- 1.7.9.5