Ver código fonte

Merge branch 'web-stack' of Tilo15/inversion into master

Billy Barrow 1 mês atrás
pai
commit
536c1a13c4
6 arquivos alterados com 147 adições e 11 exclusões
  1. 9 0
      .usmignore
  2. 40 0
      MANIFEST.usm
  3. 76 11
      src/Scope.vala
  4. 10 0
      usm-scripts/build.sh
  5. 10 0
      usm-scripts/install.sh
  6. 2 0
      usm-scripts/post-install.sh

+ 9 - 0
.usmignore

@@ -0,0 +1,9 @@
+# Build trees at any depth (directory-only: the directory and all beneath it)
+builddir*/
+
+# Database artifacts by basename at any depth
+*.sqlite*
+db.sqlite
+
+# Local agent session state (directory-only)
+.kilo/

+ 40 - 0
MANIFEST.usm

@@ -0,0 +1,40 @@
+{
+  "name": "inversion",
+  "version": "0.1",
+  "summary": "Inversion of Control and dependency injection for the Web-Stack",
+  "licences": [],
+  "flags": ["ninjaStyleProgress"],
+  "provides": {
+    "lib:libinversion-0.1.so": "as-expected",
+    "inc:inversion-0.1.h": "as-expected",
+    "vapi:inversion-0.1.vapi": "as-expected",
+    "gir:inversion-0.1.gir": "as-expected",
+    "typelib:inversion-0.1.typelib": "as-expected",
+    "pc:inversion-0.1.pc": "as-expected"
+  },
+  "depends": {
+    "runtime": [
+      "lib:libglib-2.0.so.0",
+      "lib:libgobject-2.0.so.0",
+      "lib:libinvercargill-1.so"
+    ],
+    "build": [
+      "bin:valac",
+      "bin:meson",
+      "bin:ninja",
+      "bin:gcc",
+      "bin:g-ir-compiler",
+      "pc:glib-2.0.pc",
+      "pc:gobject-2.0.pc",
+      "pc:invercargill-1.pc"
+    ],
+    "manage": [
+      "bin:bash"
+    ]
+  },
+  "execs": {
+    "build": "usm-scripts/build.sh",
+    "install": "usm-scripts/install.sh",
+    "postInstall": "usm-scripts/post-install.sh"
+  }
+}

+ 76 - 11
src/Scope.vala

@@ -17,11 +17,62 @@ namespace Inversion {
      * within that scope (not inherited by child scopes). This is useful for
      * registering request-specific context objects in web applications.
      */
+    /**
+     * Factory that resolves the scope itself from the scope.
+     *
+     * Holds only an unowned back-reference: a delegate factory here
+     * (`s => this`) would capture the scope strongly, and because the
+     * resulting registration lives in the scope's own registration
+     * catalogue every scope would own a strong reference cycle
+     * (scope → registration → factory closure → scope) that refcounting
+     * can never collect.
+     */
+    internal class ScopeSelfFactory : Object, Factory {
+
+        private unowned Scope scope;
+
+        internal ScopeSelfFactory(Scope scope) {
+            this.scope = scope;
+        }
+
+        public Object create(Scope resolution_scope) throws Error {
+            return scope;
+        }
+    }
+
+    /**
+     * Forwards a registration's alias_added signal into a scope's local
+     * registration catalogue.
+     *
+     * A direct lambda (`registration.alias_added.connect(t => ...)`)
+     * captures the registration itself; the capture block keeps a strong
+     * reference to the emitting registration, forming a self-cycle that
+     * leaks the registration (and everything its factory closure
+     * captures — typically per-request context objects) permanently.
+     * This object references both the registration and the catalogue
+     * unowned, so no cycle is formed.
+     */
+    internal class LocalAliasForwarder : Object {
+
+        private unowned Registration registration;
+        private unowned Catalogue<Type, Registration> registrations;
+
+        internal LocalAliasForwarder(Registration registration, Catalogue<Type, Registration> registrations) {
+            this.registration = registration;
+            this.registrations = registrations;
+        }
+
+        internal void on_alias_added(Type alias) {
+            registrations.add(alias, registration);
+        }
+    }
+
     public class Scope : Object {
 
         public Container container { get; construct set; }
         private Dictionary<Registration, Object> scoped_instances;
         private Catalogue<Type, Registration> local_registrations;
+        private Dictionary<Registration, LocalAliasForwarder> alias_forwarders;
         public Lifecycle scope_lifecycle { get; construct set; }
 
         /**
@@ -33,14 +84,29 @@ namespace Inversion {
             Object(container: container);
             this.scoped_instances = new Dictionary<Registration, Object>();
             this.local_registrations = new Catalogue<Type, Registration>();
+            this.alias_forwarders = new Dictionary<Registration, LocalAliasForwarder>();
             this.scope_lifecycle = lifecycle;
 
-            if(lifecycle == Lifecycle.SCOPED) {
-                register_local_scoped<Scope>(s => this);
-            }
-            else {
-                register_local_transient<Scope>(s => this);
-            }
+            var self_lifecycle = lifecycle == Lifecycle.SCOPED ? Lifecycle.SCOPED : Lifecycle.TRANSIENT;
+            register_local_factory_type(typeof(Scope), new ScopeSelfFactory(this), self_lifecycle);
+        }
+
+        /**
+         * Releases everything this scope owns.
+         *
+         * Scoped instances and local registrations routinely hold strong
+         * references back to the scope (directly via `inject<Scope>()` or
+         * indirectly through other scoped instances), so an undisposed
+         * scope is an uncollectable reference cycle and everything it
+         * created leaks. Owners of short-lived scopes — one per HTTP
+         * request, for example — must call release() when the scope's work is
+         * done. After disposal the scope can still resolve, creating
+         * fresh instances and registrations as before.
+         */
+        public void release() {
+            alias_forwarders.clear();
+            scoped_instances.clear();
+            local_registrations.clear();
         }
 
 
@@ -62,11 +128,10 @@ namespace Inversion {
             var registration = new Registration(implementation_type, factory, lifecycle);
             local_registrations.add(implementation_type, registration);
 
-            // Add subsequent aliases added to this registration
-            registration.alias_added.connect(t => {
-                local_registrations.add(t, registration);
-            });
-            
+            var forwarder = new LocalAliasForwarder(registration, local_registrations);
+            alias_forwarders[registration] = forwarder;
+            registration.alias_added.connect(forwarder.on_alias_added);
+
             return registration;
         }
 

+ 10 - 0
usm-scripts/build.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+set -e
+
+src_dir=$(pwd)
+build_dir=$1
+
+cd ${build_dir}
+
+meson setup ${src_dir} --prefix=${PREFIX} --libdir=${LIBDIR} --bindir=${BINDIR} --includedir=${INCLUDEDIR}
+ninja

+ 10 - 0
usm-scripts/install.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+set -e
+
+src_dir=$(pwd)
+build_dir=$1
+install_dir=$2
+
+cd ${build_dir}
+
+meson install --destdir ${install_dir}

+ 2 - 0
usm-scripts/post-install.sh

@@ -0,0 +1,2 @@
+#!/bin/bash
+ldconfig