|
|
@@ -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;
|
|
|
}
|
|
|
|